/************************************************************************ 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 fro 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 #include #include #include #include "oEvent.h" #include "gdioutput.h" #include "gdifonts.h" #include "oDataContainer.h" #include "MetaList.h" #include "random.h" #include "SportIdent.h" #include "meosException.h" #include "oFreeImport.h" #include "TabBase.h" #include "meos.h" #include "meos_util.h" #include "RunnerDB.h" #include "localizer.h" #include "progress.h" #include "intkeymapimpl.hpp" #include "meosdb/sqltypes.h" #include "socket.h" #include #include "MeOSFeatures.h" #include "generalresult.h" #include "oEventDraw.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// #include #include #include #include #include "Table.h" //Version of database int oEvent::dbVersion = 79; class RelativeTimeFormatter : public oDataDefiner { string name; public: RelativeTimeFormatter(const char *n) : name(n) {} const string &formatData(oBase *obj) const { int t = obj->getDCI().getInt(name); if (t <= 0) return MakeDash("-"); return obj->getEvent()->getAbsTime(t); } string setData(oBase *obj, const string &input) const { int t = obj->getEvent()->getRelativeTime(input); obj->getDI().setInt(name.c_str(), t); return formatData(obj); } int addTableColumn(Table *table, const string &description, int minWidth) const { return table->addColumn(description, max(minWidth, 90), false, true); } }; class AbsoluteTimeFormatter : public oDataDefiner { string name; public: AbsoluteTimeFormatter(const char *n) : name(n) {} const string &formatData(oBase *obj) const { int t = obj->getDCI().getInt(name); return formatTime(t); } string setData(oBase *obj, const string &input) const { int t = convertAbsoluteTimeMS(input); if (t == NOTIME) t = 0; obj->getDI().setInt(name.c_str(), t); return formatData(obj); } int addTableColumn(Table *table, const string &description, int minWidth) const { return table->addColumn(description, max(minWidth, 90), false, true); } }; oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) { readOnly = false; tLongTimesCached = -1; directSocket = 0; hMod=0; ZeroTime=0; vacantId = 0; noClubId = 0; dataRevision = 0; sqlCounterRunners=0; sqlCounterClasses=0; sqlCounterCourses=0; sqlCounterControls=0; sqlCounterClubs=0; sqlCounterCards=0; sqlCounterPunches=0; sqlCounterTeams=0; disableRecalculate = false; initProperties(); #ifndef MEOSDB listContainer = new MetaListContainer(this); #else throw std::exception(); #endif tCurrencyFactor = 1; tCurrencySymbol = "kr"; tCurrencySeparator = ","; tCurrencyPreSymbol = false; tClubDataRevision = -1; nextFreeStartNo = 0; SYSTEMTIME st; GetLocalTime(&st); char bf[64]; sprintf_s(bf, 64, "%d-%02d-%02d", st.wYear, st.wMonth, st.wDay); Date=bf; ZeroTime=st.wHour*3600; oe=this; runnerDB = new RunnerDB(this); meosFeatures = new MeOSFeatures(); openFileLock = new MeOSFileLock(); char cp[64]; DWORD size=64; GetComputerName(cp, &size); clientName=cp; HasDBConnection = false; HasPendingDBConnection = false; #ifdef BUILD_DB_DLL msSynchronizeList=0; msSynchronizeRead=0; msSynchronizeUpdate=0; msOpenDatabase=0; msRemove=0; msMonitor=0; msUploadRunnerDB = 0; msGetErrorState=0; msResetConnection=0; msReConnect=0; #endif currentNameMode = FirstLast; nextTimeLineEvent = 0; //These object must be initialized on creation of any oObject, //but we need to create (dummy) objects to get the sizeof their //oData[]-sets... // --- REMEMBER TO UPDATE dvVersion when these are changed. oEventData=new oDataContainer(dataSize); oEventData->addVariableCurrency("CardFee", "Brickhyra"); oEventData->addVariableCurrency("EliteFee", "Elitavgift"); oEventData->addVariableCurrency("EntryFee", "Normalavgift"); oEventData->addVariableCurrency("YouthFee", "Ungdomsavgift"); oEventData->addVariableInt("YouthAge", oDataContainer::oIS8U, "Åldersgräns ungdom"); oEventData->addVariableInt("SeniorAge", oDataContainer::oIS8U, "Åldersgräns äldre"); oEventData->addVariableString("Account", 30, "Konto"); oEventData->addVariableDate("PaymentDue", "Sista betalningsdatum"); oEventData->addVariableDate("OrdinaryEntry", "Ordinarie anmälningsdatum"); oEventData->addVariableString("LateEntryFactor", 6, "Avgiftshöjning (procent)"); oEventData->addVariableString("Organizer", "Arrangör"); oEventData->addVariableString("CareOf", 31, "c/o"); oEventData->addVariableString("Street", 32, "Adress"); oEventData->addVariableString("Address", 32, "Postadress"); oEventData->addVariableString("EMail", "E-post"); oEventData->addVariableString("Homepage", "Hemsida"); oEventData->addVariableString("Phone", 32, "Telefon"); oEventData->addVariableInt("UseEconomy", oDataContainer::oIS8U, "Ekonomi"); oEventData->addVariableInt("UseSpeaker", oDataContainer::oIS8U, "Speaker"); oEventData->addVariableInt("SkipRunnerDb", oDataContainer::oIS8U, "Databas"); oEventData->addVariableInt("ExtId", oDataContainer::oIS64, "Externt Id"); oEventData->addVariableInt("MaxTime", oDataContainer::oISTime, "Gräns för maxtid"); oEventData->addVariableString("PreEvent", sizeof(CurrentNameId), ""); oEventData->addVariableString("PostEvent", sizeof(CurrentNameId), ""); // Positive number -> stage number, negative number -> no stage number. Zero = unknown oEventData->addVariableInt("EventNumber", oDataContainer::oIS8, ""); oEventData->addVariableInt("CurrencyFactor", oDataContainer::oIS16, "Valutafaktor"); oEventData->addVariableString("CurrencySymbol", 5, "Valutasymbol"); oEventData->addVariableString("CurrencySeparator", 2, "Decimalseparator"); oEventData->addVariableInt("CurrencyPreSymbol", oDataContainer::oIS8, "Symbolläge"); oEventData->addVariableString("CurrencyCode", 5, "Valutakod"); oEventData->addVariableInt("UTC", oDataContainer::oIS8, "UTC"); oEventData->addVariableInt("Analysis", oDataContainer::oIS8, "Utan analys"); // With split time analysis (0 = default, with analysis, with min/km) // bit 1: without analysis // bit 2: without min/km // bit 4: without result oEventData->addVariableString("SPExtra", "Extra rader"); oEventData->addVariableString("IVExtra", "Fakturainfo"); oEventData->addVariableString("Features", "Funktioner"); oEventData->addVariableString("EntryExtra", "Extra rader"); oEventData->addVariableInt("NumStages", oDataContainer::oIS8, "Antal etapper"); oEventData->addVariableInt("BibGap", oDataContainer::oIS8U, "Nummerlappshopp"); oEventData->addVariableInt("LongTimes", oDataContainer::oIS8U, "Långa tider"); oEventData->addVariableString("PayModes", "Betalsätt"); oEventData->initData(this, dataSize); oClubData=new oDataContainer(oClub::dataSize); oClubData->addVariableInt("District", oDataContainer::oIS32, "Organisation"); oClubData->addVariableString("ShortName", 8, "Kortnamn"); oClubData->addVariableString("CareOf", 31, "c/o"); oClubData->addVariableString("Street", 41, "Gata"); oClubData->addVariableString("City", 23, "Stad"); oClubData->addVariableString("State", 23, "Region"); oClubData->addVariableString("ZIP", 11, "Postkod"); oClubData->addVariableString("EMail", 64, "E-post"); oClubData->addVariableString("Phone", 32, "Telefon"); oClubData->addVariableString("Nationality", 3, "Nationalitet"); oClubData->addVariableString("Country", 23, "Land"); oClubData->addVariableString("Type", 20, "Typ"); oClubData->addVariableInt("ExtId", oDataContainer::oIS64, "Externt Id"); vector< pair > eInvoice; eInvoice.push_back(make_pair("E", "Elektronisk")); eInvoice.push_back(make_pair("A", "Elektronisk godkänd")); eInvoice.push_back(make_pair("P", "Ej elektronisk")); eInvoice.push_back(make_pair("", MakeDash("-"))); oClubData->addVariableEnum("Invoice", 1, "Faktura", eInvoice); oClubData->addVariableInt("InvoiceNo", oDataContainer::oIS16U, "Fakturanummer"); oRunnerData=new oDataContainer(oRunner::dataSize); oRunnerData->addVariableCurrency("Fee", "Anm. avgift"); oRunnerData->addVariableCurrency("CardFee", "Brickhyra"); oRunnerData->addVariableCurrency("Paid", "Betalat"); oRunnerData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt"); oRunnerData->addVariableCurrency("Taxable", "Skattad avgift"); oRunnerData->addVariableInt("BirthYear", oDataContainer::oIS32, "Födelseår"); oRunnerData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; oRunnerData->addVariableInt("Rank", oDataContainer::oIS16U, "Ranking"); //oRunnerData->addVariableInt("VacRank", oDataContainer::oIS16U, "Vak. ranking"); oRunnerData->addVariableDate("EntryDate", "Anm. datum"); vector< pair > sex; sex.push_back(make_pair("M", "Man")); sex.push_back(make_pair("F", "Kvinna")); sex.push_back(make_pair("", MakeDash("-"))); oRunnerData->addVariableEnum("Sex", 1, "Kön", sex); oRunnerData->addVariableString("Nationality", 3, "Nationalitet"); oRunnerData->addVariableString("Country", 23, "Land"); oRunnerData->addVariableInt("ExtId", oDataContainer::oIS64, "Externt Id"); oRunnerData->addVariableInt("Priority", oDataContainer::oIS8U, "Prioritering"); oRunnerData->addVariableString("Phone", 20, "Telefon"); oRunnerData->addVariableInt("RaceId", oDataContainer::oIS32, "Lopp-id", &oRunner::raceIdFormatter); oRunnerData->addVariableInt("TimeAdjust", oDataContainer::oIS16, "Tidsjustering"); oRunnerData->addVariableInt("PointAdjust", oDataContainer::oIS32, "Poängjustering"); oRunnerData->addVariableInt("TransferFlags", oDataContainer::oIS32, "Överföring"); oRunnerData->addVariableInt("Shorten", oDataContainer::oIS8U, "Avkortning"); oRunnerData->addVariableInt("EntrySource", oDataContainer::oIS32, "Källa"); oRunnerData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); oControlData=new oDataContainer(oControl::dataSize); oControlData->addVariableInt("TimeAdjust", oDataContainer::oIS32, "Tidsjustering"); oControlData->addVariableInt("MinTime", oDataContainer::oIS32, "Minitid"); oControlData->addVariableDecimal("xpos", "x", 1); oControlData->addVariableDecimal("ypos", "y", 1); oControlData->addVariableDecimal("latcrd", "Latitud", 6); oControlData->addVariableDecimal("longcrd", "Longitud", 6); oControlData->addVariableInt("Rogaining", oDataContainer::oIS32, "Poäng"); oControlData->addVariableInt("Radio", oDataContainer::oIS8U, "Radio"); oCourseData=new oDataContainer(oCourse::dataSize); oCourseData->addVariableInt("NumberMaps", oDataContainer::oIS16, "Kartor"); oCourseData->addVariableString("StartName", 16, "Start"); oCourseData->addVariableInt("Climb", oDataContainer::oIS16, "Stigning"); oCourseData->addVariableInt("StartIndex", oDataContainer::oIS32, "Startindex"); oCourseData->addVariableInt("FinishIndex", oDataContainer::oIS32, "Målindex"); oCourseData->addVariableInt("RPointLimit", oDataContainer::oIS32, "Poänggräns"); oCourseData->addVariableInt("RTimeLimit", oDataContainer::oIS32, "Tidsgräns"); oCourseData->addVariableInt("RReduction", oDataContainer::oIS32, "Poängreduktion"); oCourseData->addVariableInt("RReductionMethod", oDataContainer::oIS8U, "Reduktionsmetod"); oCourseData->addVariableInt("FirstAsStart", oDataContainer::oIS8U, "Från första"); oCourseData->addVariableInt("LastAsFinish", oDataContainer::oIS8U, "Till sista"); oCourseData->addVariableInt("CControl", oDataContainer::oIS16U, "Varvningskontroll"); //Common control index oCourseData->addVariableInt("Shorten", oDataContainer::oIS32, "Avkortning"); oClassData=new oDataContainer(oClass::dataSize); oClassData->addVariableInt("ExtId", oDataContainer::oIS64, "Externt Id"); oClassData->addVariableString("LongName", 32, "Långt namn"); oClassData->addVariableInt("LowAge", oDataContainer::oIS8U, "Undre ålder"); oClassData->addVariableInt("HighAge", oDataContainer::oIS8U, "Övre ålder"); oClassData->addVariableInt("HasPool", oDataContainer::oIS8U, "Banpool"); oClassData->addVariableInt("AllowQuickEntry", oDataContainer::oIS8U, "Direktanmälan"); oClassData->addVariableString("ClassType", 40, "Klasstyp"); vector< pair > sexClass; sexClass.push_back(make_pair("M", "Män")); sexClass.push_back(make_pair("F", "Kvinnor")); sexClass.push_back(make_pair("B", "Alla")); sexClass.push_back(make_pair("", MakeDash("-"))); oClassData->addVariableEnum("Sex", 1, "Kön", sexClass); oClassData->addVariableString("StartName", 16, "Start"); oClassData->addVariableInt("StartBlock", oDataContainer::oIS8U, "Block"); oClassData->addVariableInt("NoTiming", oDataContainer::oIS8U, "Ej tidtagning"); oClassData->addVariableInt("FreeStart", oDataContainer::oIS8U, "Fri starttid"); oClassData->addVariableInt("IgnoreStart", oDataContainer::oIS8U, "Ej startstämpling"); firstStartDefiner = new RelativeTimeFormatter("FirstStart"); oClassData->addVariableInt("FirstStart", oDataContainer::oIS32, "Första start", firstStartDefiner); intervalDefiner = new AbsoluteTimeFormatter("StartInterval"); oClassData->addVariableInt("StartInterval", oDataContainer::oIS16, "Intervall", intervalDefiner); oClassData->addVariableInt("Vacant", oDataContainer::oIS8U, "Vakanser"); oClassData->addVariableInt("Reserved", oDataContainer::oIS16U, "Extraplatser"); oClassData->addVariableCurrency("ClassFee", "Anm. avgift"); oClassData->addVariableCurrency("HighClassFee", "Efteranm. avg."); oClassData->addVariableCurrency("ClassFeeRed", "Reducerad avg."); oClassData->addVariableCurrency("HighClassFeeRed", "Red. avg. efteranm."); oClassData->addVariableInt("SortIndex", oDataContainer::oIS32, "Sortering"); oClassData->addVariableInt("MaxTime", oDataContainer::oISTime, "Maxtid"); vector< pair > statusClass; statusClass.push_back(make_pair("", "OK")); statusClass.push_back(make_pair("IR", "Struken med återbetalning")); statusClass.push_back(make_pair("I", "Struken utan återbetalning")); oClassData->addVariableEnum("Status", 2, "Status", statusClass); oClassData->addVariableInt("DirectResult", oDataContainer::oIS8, "Resultat vid målstämpling"); oClassData->addVariableString("Bib", 8, "Nummerlapp"); vector< pair > bibMode; bibMode.push_back(make_pair("", "Från lag")); bibMode.push_back(make_pair("A", "Lag + sträcka")); bibMode.push_back(make_pair("F", "Fritt")); oClassData->addVariableEnum("BibMode", 1, "Nummerlappshantering", bibMode); oClassData->addVariableInt("Unordered", oDataContainer::oIS8U, "Oordnade parallella"); oClassData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); oTeamData = new oDataContainer(oTeam::dataSize); oTeamData->addVariableCurrency("Fee", "Anm. avgift"); oTeamData->addVariableCurrency("Paid", "Betalat"); oTeamData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt"); oTeamData->addVariableCurrency("Taxable", "Skattad avgift"); oTeamData->addVariableDate("EntryDate", "Anm. datum"); oTeamData->addVariableString("Nationality", 3, "Nationalitet"); oTeamData->addVariableString("Country", 23, "Land"); oTeamData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; oTeamData->addVariableInt("ExtId", oDataContainer::oIS64, "Externt Id"); oTeamData->addVariableInt("Priority", oDataContainer::oIS8U, "Prioritering"); oTeamData->addVariableInt("SortIndex", oDataContainer::oIS16, "Sortering"); oTeamData->addVariableInt("TimeAdjust", oDataContainer::oIS16, "Tidsjustering"); oTeamData->addVariableInt("PointAdjust", oDataContainer::oIS32, "Poängjustering"); oTeamData->addVariableInt("TransferFlags", oDataContainer::oIS32, "Överföring"); oTeamData->addVariableInt("EntrySource", oDataContainer::oIS32, "Källa"); oTeamData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); generalResults.push_back(GeneralResultCtr("atcontrol", "Result at a control", new ResultAtControl())); generalResults.push_back(GeneralResultCtr("totatcontrol", "Total/team result at a control", new TotalResultAtControl())); currentClientCS = 0; memset(CurrentFile, 0, sizeof(CurrentFile)); } oEvent::~oEvent() { //Clean up things in the right order. clear(); delete runnerDB; delete meosFeatures; runnerDB = 0; meosFeatures = 0; if (hMod) { HasDBConnection=false; FreeLibrary(hMod); hMod=0; } delete oEventData; delete oRunnerData; delete oClubData; delete oControlData; delete oCourseData; delete oClassData; delete oTeamData; delete openFileLock; delete listContainer; return; } void oEvent::initProperties() { setProperty("TextSize", getPropertyString("TextSize", "0")); setProperty("Language", getPropertyString("Language", "103")); setProperty("Interactive", getPropertyString("Interactive", "1")); setProperty("Database", getPropertyString("Database", "1")); // Setup some defaults getPropertyInt("SplitLateFees", false); getPropertyInt("DirectPort", 21338); getPropertyInt("UseHourFormat", 1); getPropertyInt("UseDirectSocket", true); getPropertyInt("UseEventorUTC", 0); } void oEvent::listProperties(bool userProps, vector< pair > &propNames) const { set filter; if (userProps) { filter.insert("Language"); filter.insert("apikey"); filter.insert("Colors"); filter.insert("xpos"); filter.insert("ypos"); filter.insert("xsize"); filter.insert("ysize"); filter.insert("ListType"); filter.insert("LastCompetition"); filter.insert("DrawTypeDefault"); filter.insert("Email"); filter.insert("TextSize"); filter.insert("PayModes"); } // Boolean and integer properties set b, i; // Booleans b.insert("AdvancedClassSettings"); b.insert("AutoTie"); b.insert("CurrencyPreSymbol"); b.insert("Database"); b.insert("Interactive"); b.insert("intertime"); b.insert("ManualInput"); b.insert("PageBreak"); b.insert("RentCard"); b.insert("SpeakerShortNames"); b.insert("splitanalysis"); b.insert("UseDirectSocket"); b.insert("UseEventor"); b.insert("UseEventorUTC"); b.insert("UseHourFormat"); b.insert("SplitLateFees"); b.insert("WideSplitFormat"); b.insert("pagebreak"); b.insert("FirstTime"); b.insert("ExportCSVSplits"); // Integers i.insert("YouthFee"); i.insert("YouthAge"); i.insert("TextSize"); i.insert("SynchronizationTimeOut"); i.insert("SeniorAge"); i.insert("Port"); i.insert("MaximumSpeakerDelay"); i.insert("FirstInvoice"); i.insert("EntryFee"); i.insert("EliteFee"); i.insert("DirectPort"); i.insert("DatabaseUpdate"); i.insert("ControlTo"); i.insert("ControlFrom"); i.insert("CardFee"); i.insert("addressypos"); i.insert("addressxpos"); i.insert("AutoSaveTimeOut"); propNames.clear(); for(map::const_iterator it = eventProperties.begin(); it != eventProperties.end(); ++it) { if (!filter.count(it->first)) { if (b.count(it->first)) { assert(!i.count(it->first)); propNames.push_back(make_pair(it->first, Boolean)); } else if (i.count(it->first)) { propNames.push_back(make_pair(it->first, Integer)); } else propNames.push_back(make_pair(it->first, String)); } } } pControl oEvent::addControl(int Id, int Number, const string &Name) { if (Id<=0) Id=getFreeControlId(); else qFreeControlId = max (qFreeControlId, Id); oControl c(this); c.set(Id, Number, Name); Controls.push_back(c); oe->updateTabs(); return &Controls.back(); } int oEvent::getNextControlNumber() const { int c = 31; for (oControlList::const_iterator it = Controls.begin(); it!=Controls.end(); ++it) c = max(c, it->maxNumber()+1); return c; } pControl oEvent::addControl(const oControl &oc) { if (oc.Id<=0) return 0; if (getControl(oc.Id, false)) return 0; qFreeControlId = max (qFreeControlId, Id); Controls.push_back(oc); return &Controls.back(); } DirectSocket &oEvent::getDirectSocket() { if (directSocket == 0) directSocket = new DirectSocket(getId(), getPropertyInt("DirectPort", 21338)); return *directSocket; } pControl oEvent::getControl(int Id) const { return const_cast(this)->getControl(Id, false); } pControl oEvent::getControl(int Id, bool create) { oControlList::const_iterator it; for (it=Controls.begin(); it != Controls.end(); ++it) { if (it->Id==Id) return pControl(&*it); } if (!create || Id<=0) return 0; //Not found. Auto add... return addControl(Id, Id, ""); } bool oEvent::writeControls(xmlparser &xml) { oControlList::iterator it; xml.startTag("ControlList"); for (it=Controls.begin(); it != Controls.end(); ++it) it->write(xml); xml.endTag(); return true; } bool oEvent::writeCourses(xmlparser &xml) { oCourseList::iterator it; xml.startTag("CourseList"); for (it=Courses.begin(); it != Courses.end(); ++it) it->Write(xml); xml.endTag(); return true; } bool oEvent::writeClasses(xmlparser &xml) { oClassList::iterator it; xml.startTag("ClassList"); for (it=Classes.begin(); it != Classes.end(); ++it) it->Write(xml); xml.endTag(); return true; } bool oEvent::writeClubs(xmlparser &xml) { oClubList::iterator it; xml.startTag("ClubList"); for (it=Clubs.begin(); it != Clubs.end(); ++it) it->write(xml); xml.endTag(); return true; } bool oEvent::writeRunners(xmlparser &xml, ProgressWindow &pw) { oRunnerList::iterator it; xml.startTag("RunnerList"); int k=0; for (it=Runners.begin(); it != Runners.end(); ++it) { if (!it->tDuplicateLeg) //Duplicates is written by the ruling runner. it->Write(xml); if (++k%300 == 200) pw.setSubProgress( (1000*k)/ Runners.size()); } xml.endTag(); return true; } bool oEvent::writePunches(xmlparser &xml, ProgressWindow &pw) { oFreePunchList::iterator it; xml.startTag("PunchList"); int k = 0; for (it=punches.begin(); it != punches.end(); ++it) { it->Write(xml); if (++k%300 == 200) pw.setSubProgress( (1000*k)/ Runners.size()); } xml.endTag(); return true; } //Write free cards not owned by a runner. bool oEvent::writeCards(xmlparser &xml) { oCardList::iterator it; xml.startTag("CardList"); for (it=Cards.begin(); it != Cards.end(); ++it) { if (it->getOwner() == 0) it->Write(xml); } xml.endTag(); return true; } void oEvent::duplicate() { char file[260]; char filename[64]; char nameid[64]; SYSTEMTIME st; GetLocalTime(&st); sprintf_s(filename, 64, "meos_%d%02d%02d_%02d%02d%02d_%X.meos", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); getUserFile(file, filename); _splitpath_s(filename, NULL, 0, NULL,0, nameid, 64, NULL, 0); int i=0; while (nameid[i]) { if (nameid[i]=='.') { nameid[i]=0; break; } i++; } char oldFile[260]; char oldId[64]; strcpy_s(oldFile, CurrentFile); strcpy_s(oldId, CurrentNameId); string oldAnno = getAnnotation(); strcpy_s(CurrentFile, file); strcpy_s(CurrentNameId, nameid); sprintf_s(filename, "%d/%d %d:%02d", st.wDay, st.wMonth, st.wHour, st.wMinute); string anno = lang.tl("Kopia (X)#" + string(filename)); anno = oldAnno.empty() ? anno : oldAnno + " " + anno; setAnnotation(anno); try { save(); } catch(...) { // Restore in case of error strcpy_s(CurrentFile, oldFile); strcpy_s(CurrentNameId, oldId); setAnnotation(oldAnno); throw; } // Restore strcpy_s(CurrentFile, oldFile); strcpy_s(CurrentNameId, oldId); setAnnotation(oldAnno); } bool oEvent::save() { if (empty() || gdibase.isTest()) return true; autoSynchronizeLists(true); if (!CurrentFile[0]) throw std::exception("Felaktigt filnamn"); int f=0; _sopen_s(&f, CurrentFile, _O_RDONLY, _SH_DENYNO, _S_IWRITE); char fn1[260]; char fn2[260]; string finalRenameTarget; if (f!=-1) { _close(f); time_t currentTime = time(0); const int baseAge = 3; // Three minutes time_t allowedAge = baseAge*60; time_t oldAge = allowedAge + 60; const int maxBackup = 8; int toDelete = maxBackup; for(int k = 0; k <= maxBackup; k++) { sprintf_s(fn1, MAX_PATH, "%s.bu%d", CurrentFile, k); struct stat st; int ret = stat(fn1, &st); if (ret==0) { time_t age = currentTime - st.st_mtime; // If file is too young or to old at its // position, it is possible to delete. // The oldest old file (or youngest young file if none is old) // possible to delete is deleted. // If no file is possible to delete, the oldest // file is deleted. if ( (ageoldAge) toDelete = k; allowedAge *= 2; oldAge*=2; if (k==maxBackup-3) oldAge = 24*3600; // Allow a few old copies } else { toDelete = k; // File does not exist. No file need be deleted break; } } sprintf_s(fn1, MAX_PATH, "%s.bu%d", CurrentFile, toDelete); ::remove(fn1); for(int k=toDelete;k>0;k--) { sprintf_s(fn1, MAX_PATH, "%s.bu%d", CurrentFile, k-1); sprintf_s(fn2, MAX_PATH, "%s.bu%d", CurrentFile, k); rename(fn1, fn2); } finalRenameTarget = fn1; //rename(CurrentFile, fn1); } bool res; if (finalRenameTarget.empty()) { res = save(CurrentFile); if (!(HasDBConnection || HasPendingDBConnection)) openFileLock->lockFile(CurrentFile); } else { string tmpName = string(CurrentFile) + ".~tmp"; res = save(tmpName); if (res) { openFileLock->unlockFile(); rename(CurrentFile, finalRenameTarget.c_str()); rename(tmpName.c_str(), CurrentFile); if (!(HasDBConnection || HasPendingDBConnection)) openFileLock->lockFile(CurrentFile); } } return res; } bool oEvent::save(const string &fileIn) { if (gdibase.isTest()) return true; const char *file = fileIn.c_str(); xmlparser xml(0); ProgressWindow pw(gdibase.getHWND()); if (Runners.size()>200) pw.init(); xml.openOutput(file, true); xml.startTag("meosdata", "version", getMajorVersion()); xml.write("Name", Name); xml.write("Date", Date); xml.write("ZeroTime", ZeroTime); xml.write("NameId", CurrentNameId); xml.write("Annotation", Annotation); xml.write("Id", Id); xml.write("Updated", Modified.getStamp()); oEventData->write(this, xml); int i = 0; vector p; p.resize(10); p[0] = 2; //= {2, 20, 50, 80, 180, 400,500,700,800,1000}; p[1] = Controls.size(); p[2] = Courses.size(); p[3] = Classes.size(); p[4] = Clubs.size(); p[5] = Runners.size() + Cards.size(); p[6] = Teams.size(); p[7] = punches.size(); p[8] = Cards.size(); p[9] = Runners.size()/2; int sum = 0; for (int k = 0; k<10; k++) sum += p[k]; for (int k = 1; k<10; k++) p[k] = p[k-1] + (1000 * p[k]) / sum; p[9] = 1000; pw.setProgress(p[i++]); writeControls(xml); pw.setProgress(p[i++]); writeCourses(xml); pw.setProgress(p[i++]); writeClasses(xml); pw.setProgress(p[i++]); writeClubs(xml); pw.initSubProgress(p[i], p[i+1]); pw.setProgress(p[i++]); writeRunners(xml, pw); pw.setProgress(p[i++]); writeTeams(xml); pw.initSubProgress(p[i], p[i+1]); pw.setProgress(p[i++]); writePunches(xml, pw); pw.setProgress(p[i++]); writeCards(xml); xml.startTag("Lists"); listContainer->save(MetaListContainer::ExternalList, xml, this); xml.endTag(); xml.closeOut(); pw.setProgress(p[i++]); updateRunnerDatabase(); pw.setProgress(p[i++]); return true; } string oEvent::getNameId(int id) const { if (id == 0) return CurrentNameId; list::const_iterator it; for (it=cinfo.begin(); it!=cinfo.end(); ++it) { if (it->Server.empty()) { if (id == it->Id) return it->NameId; } else if (!it->Server.empty()) { if (id == (10000000+it->Id)) { return it->NameId; } } } return _EmptyString; } const string &oEvent::getFileNameFromId(int id) const { list::const_iterator it; for (it=cinfo.begin(); it!=cinfo.end(); ++it) { if (it->Server.empty()) { if (id == it->Id) return it->FullPath; } else if (!it->Server.empty()) { if (id == (10000000+it->Id)) { return _EmptyString; } } } return _EmptyString; } bool oEvent::open(int id) { list::iterator it; for (it=cinfo.begin(); it!=cinfo.end(); ++it) { if (it->Server.empty()) { if (id == it->Id) { CompetitionInfo ci=*it; //Take copy return open(ci.FullPath.c_str()); } } else if (!it->Server.empty()) { if (id == (10000000+it->Id)) { CompetitionInfo ci=*it; //Take copy return readSynchronize(ci); } } } return false; } static DWORD timer; static string mlog; static void tic() { timer = GetTickCount(); mlog.clear(); } static void toc(const string &str) { DWORD t = GetTickCount(); if (!mlog.empty()) mlog += ",\n"; else mlog = "Tid (hundradels sekunder):\n"; mlog += str + "=" + itos( (t-timer)/10 ); timer = t; } bool oEvent::open(const string &file, bool Import) { if (!Import) openFileLock->lockFile(file); xmlparser xml(0); xml.setProgress(gdibase.getHWND()); tic(); string log; xml.read(file); xmlattrib ver = xml.getObject(0).getAttrib("version"); if (ver) { string vs = ver.get(); if (vs > getMajorVersion()) { // Tävlingen är skapad i MeOS X. Data kan gå förlorad om du öppnar tävlingen.\n\nVill du fortsätta? bool cont = gdibase.ask("warn:opennewversion#" + vs); if (!cont) return false; } } toc("parse"); //This generates a new file name newCompetition("-"); if (!Import) { strcpy_s(CurrentFile, MAX_PATH, file.c_str()); //Keep new file name, if imported _splitpath_s(CurrentFile, NULL, 0, NULL,0, CurrentNameId, 64, NULL, 0); int i=0; while (CurrentNameId[i]) { if (CurrentNameId[i]=='.') { CurrentNameId[i]=0; break; } i++; } } bool res = open(xml); if (res && !Import) openFileLock->lockFile(file); return res; } void oEvent::restoreBackup() { string cfile = string(CurrentFile) + ".meos"; strcpy_s(CurrentFile, cfile.c_str()); } bool oEvent::open(const xmlparser &xml) { xmlobject xo; xo = xml.getObject("Date"); if (xo) Date=xo.get(); xo = xml.getObject("Name"); if (xo) Name=xo.get(); xo = xml.getObject("Annotation"); if (xo) Annotation = xo.get(); xo=xml.getObject("ZeroTime"); if (xo) ZeroTime=xo.getInt(); xo=xml.getObject("Id"); if (xo) Id=xo.getInt(); xo=xml.getObject("oData"); if (xo) oEventData->set(this, xo); setCurrency(-1, "", ",", false); xo = xml.getObject("NameId"); if (xo) strncpy_s(CurrentNameId, xo.get(), sizeof(CurrentNameId)); toc("event"); //Get controls xo = xml.getObject("ControlList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; set knownControls; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Control")){ oControl c(this); c.set(&*it); if (c.Id>0 && knownControls.count(c.Id) == 0) { Controls.push_back(c); knownControls.insert(c.Id); } } } } toc("controls"); //Get courses xo=xml.getObject("CourseList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; set knownCourse; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Course")){ oCourse c(this); c.Set(&*it); if (c.Id>0 && knownCourse.count(c.Id) == 0) { addCourse(c); knownCourse.insert(c.Id); } } } } toc("course"); //Get classes xo=xml.getObject("ClassList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; set knownClass; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Class")){ oClass c(this); c.Set(&*it); if (c.Id>0 && knownClass.count(c.Id) == 0) { Classes.push_back(c); knownClass.insert(c.Id); } } } } toc("class"); //Get clubs xo=xml.getObject("ClubList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Club")){ oClub c(this); c.set(*it); if (c.Id>0) addClub(c);//Clubs.push_back(c); } } } toc("club"); //Get runners xo=xml.getObject("RunnerList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Runner")){ oRunner r(this, 0); r.Set(*it); if (r.Id>0) addRunner(r, false); else if (r.Card) r.Card->tOwner=0; } } } toc("runner"); //Get teams xo=xml.getObject("TeamList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Team")){ oTeam t(this, 0); t.set(*it); if (t.Id>0){ Teams.push_back(t); teamById[t.Id] = &Teams.back(); Teams.back().apply(false, 0, true); } } } } toc("team"); xo=xml.getObject("PunchList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; if (xl.size() > 10) setupCardHash(false); // This improves performance when there are many cards. oFreePunch::disableHashing = true; try { for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Punch")){ oFreePunch p(this, 0, 0, 0); p.Set(&*it); addFreePunch(p); } } } catch(...) { oFreePunch::disableHashing = false; throw; } oFreePunch::disableHashing = false; oFreePunch::rehashPunches(*this, 0, 0); setupCardHash(true); // Clear } toc("punch"); xo=xml.getObject("CardList"); if (xo){ xmlList xl; xo.getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("Card")){ oCard c(this); c.Set(*it); assert(c.Id>=0); Cards.push_back(c); } } } toc("card"); xo=xml.getObject("Updated"); if (xo) Modified.setStamp(xo.get()); adjustTeamMultiRunners(0); updateFreeId(); reEvaluateAll(set(), true); //True needed to update data for sure toc("update"); string err; try { xmlobject xList = xml.getObject("Lists"); if (xList) { if (!listContainer->load(MetaListContainer::ExternalList, xList, true)) { err = "Visa listor är gjorda i en senare version av MeOS och kunde inte laddas."; } } } catch (const std::exception &ex) { if (err.empty()) err = ex.what(); } getMeOSFeatures().deserialize(getDCI().getString("Features"), *this); if (!err.empty()) throw meosException(err); return true; } bool oEvent::openRunnerDatabase(char* filename) { char file[260]; getUserFile(file, filename); char fclub[260]; char frunner[260]; strcpy_s(fclub, file); strcat_s(fclub, ".clubs"); strcpy_s(frunner, file); strcat_s(frunner, ".persons"); try { if (fileExist(fclub) && fileExist(frunner)) { runnerDB->loadClubs(fclub); runnerDB->loadRunners(frunner); } } catch(std::exception &ex) { MessageBox(0, ex.what(), "Error", MB_OK); } return true; } pRunner oEvent::dbLookUpById(__int64 extId) const { if (!useRunnerDb()) return 0; oEvent *toe = const_cast(this); static oRunner sRunner = oRunner(toe, 0); sRunner = oRunner(toe, 0); RunnerDBEntry *dbr = runnerDB->getRunnerById(int(extId)); if (dbr != 0) { sRunner.init(*dbr); /*dbr->getName(sRunner.Name); sRunner.CardNo = dbr->cardNo; sRunner.Club = runnerDB->getClub(dbr->clubNo); sRunner.getDI().setString("Nationality", dbr->getNationality()); sRunner.getDI().setInt("BirthYear", dbr->getBirthYear()); sRunner.getDI().setString("Sex", dbr->getSex()); sRunner.setExtIdentifier(dbr->getExtId());*/ return &sRunner; } else return 0; } pRunner oEvent::dbLookUpByCard(int cardNo) const { if (!useRunnerDb()) return 0; oEvent *toe = const_cast(this); static oRunner sRunner = oRunner(toe, 0); sRunner = oRunner(toe, 0); RunnerDBEntry *dbr = runnerDB->getRunnerByCard(cardNo); if (dbr != 0) { dbr->getName(sRunner.sName); oRunner::getRealName(sRunner.sName, sRunner.tRealName); sRunner.init(*dbr); sRunner.CardNo = cardNo; return &sRunner; } else return 0; } pRunner oEvent::dbLookUpByName(const string &name, int clubId, int classId, int birthYear) const { if (!useRunnerDb()) return 0; oEvent *toe = const_cast(this); static oRunner sRunner = oRunner(toe, 0); sRunner = oRunner(toe, 0); if (birthYear == 0) { pClass pc = getClass(classId); int expectedAge = pc ? pc->getExpectedAge() : 0; if (expectedAge>0) birthYear = getThisYear() - expectedAge; } pClub pc = getClub(clubId); if (pc && pc->getExtIdentifier()>0) clubId = (int)pc->getExtIdentifier(); RunnerDBEntry *dbr = runnerDB->getRunnerByName(name, clubId, birthYear); if (dbr) { sRunner.init(*dbr); /* dbr->getName(sRunner.Name); sRunner.CardNo = dbr->cardNo; sRunner.Club = runnerDB->getClub(dbr->clubNo); sRunner.getDI().setString("Nationality", dbr->getNationality()); sRunner.getDI().setInt("BirthYear", dbr->getBirthYear()); sRunner.getDI().setString("Sex", dbr->getSex());*/ sRunner.setExtIdentifier(int(dbr->getExtId())); return &sRunner; } return 0; } bool oEvent::saveRunnerDatabase(char *filename, bool onlyLocal) { char file[260]; getUserFile(file, filename); char fclub[260]; char frunner[260]; strcpy_s(fclub, file); strcat_s(fclub, ".clubs"); strcpy_s(frunner, file); strcat_s(frunner, ".persons"); if (!onlyLocal || !runnerDB->isFromServer()) { runnerDB->saveClubs(fclub); runnerDB->saveRunners(frunner); } return true; } void oEvent::updateRunnerDatabase() { if (Name=="!TESTTÄVLING") return; if (useRunnerDb()) { oRunnerList::iterator it; map clubIdMap; for (it=Runners.begin(); it != Runners.end(); ++it){ if (it->Card && it->Card->cardNo == it->CardNo && it->getDI().getInt("CardFee")==0 && it->Card->getNumPunches()>7) updateRunnerDatabase(&*it, clubIdMap); } runnerDB->refreshTables(); } if (listContainer) { for (int k = 0; k < listContainer->getNumLists(); k++) { if (listContainer->isExternal(k)) { MetaList &ml = listContainer->getList(k); string uid = ml.getUniqueId() + ".meoslist"; char file[260]; getUserFile(file, uid.c_str()); if (!fileExist(file)) { ml.save(file, this); } } } } } void oEvent::updateRunnerDatabase(pRunner r, map &clubIdMap) { if (!r->CardNo) return; runnerDB->updateAdd(*r, clubIdMap); } pCourse oEvent::addCourse(const string &pname, int plengh, int id) { oCourse c(this, id); c.Length = plengh; c.Name = pname; return addCourse(c); } pCourse oEvent::addCourse(const oCourse &oc) { if (oc.Id==0) return 0; else { pCourse pOld=getCourse(oc.getId()); if (pOld) return 0; } Courses.push_back(oc); qFreeCourseId=max(qFreeCourseId, oc.getId()); pCourse pc = &Courses.back(); if (!pc->existInDB()) { pc->updateChanged(); pc->synchronize(); } courseIdIndex[oc.Id] = pc; return pc; } void oEvent::autoAddTeam(pRunner pr) { //Warning: make sure there is no team already in DB that has not yet been applied yet... if (pr && pr->Class) { pClass pc = pr->Class; if (pc->isSingleRunnerMultiStage()) { //Auto create corresponding team pTeam t = addTeam(pr->getName(), pr->getClubId(), pc->getId()); if (pr->StartNo == 0) pr->StartNo = Teams.size(); t->setStartNo(pr->StartNo, false); t->setRunner(0, pr, true); } } } void oEvent::autoRemoveTeam(pRunner pr) { if (pr && pr->Class) { pClass pc = pr->Class; if (pc->isSingleRunnerMultiStage()) { if (pr->tInTeam) { // A team may have more than this runner -> do not remove bool canRemove = true; const vector &runners = pr->tInTeam->Runners; for (size_t k = 0; ksName != pr->sName) canRemove = false; } if (canRemove) removeTeam(pr->tInTeam->getId()); } } } } pRunner oEvent::addRunner(const string &name, int clubId, int classId, int cardNo, int birthYear, bool autoAdd) { if (birthYear != 0) birthYear = extendYear(birthYear); pRunner db_r = oe->dbLookUpByCard(cardNo); if (db_r && !db_r->matchName(name)) db_r = 0; // "Existing" card, but different runner if (db_r == 0 && getNumberSuffix(name) == 0) db_r = oe->dbLookUpByName(name, clubId, classId, birthYear); if (db_r) { // We got name from DB. Other parameters might have changed from DB. if (clubId>0) db_r->Club = getClub(clubId); db_r->Class = getClass(classId); if (cardNo>0) db_r->CardNo = cardNo; if (birthYear>0) db_r->setBirthYear(birthYear); return addRunnerFromDB(db_r, classId, autoAdd); } oRunner r(this); r.sName = name; oRunner::getRealName(r.sName, r.tRealName); r.Club = getClub(clubId); r.Class = getClass(classId); if (cardNo>0) r.CardNo = cardNo; if (birthYear>0) r.setBirthYear(birthYear); pRunner pr = addRunner(r, true); if (pr->getDI().getInt("EntryDate") == 0) pr->getDI().setDate("EntryDate", getLocalDate()); if (pr->Class) { int heat = pr->Class->getDCI().getInt("Heat"); if (heat != 0) pr->getDI().setInt("Heat", heat); } pr->updateChanged(); if (autoAdd) autoAddTeam(pr); return pr; } pRunner oEvent::addRunner(const string &pname, const string &pclub, int classId, int cardNo, int birthYear, bool autoAdd) { if (!pclub.empty() || getMeOSFeatures().hasFeature(MeOSFeatures::Clubs)) { pClub club = getClubCreate(0, pclub); return addRunner(pname, club->getId(), classId, cardNo, birthYear, autoAdd); } else return addRunner(pname, 0, classId, cardNo, birthYear, autoAdd); } pRunner oEvent::addRunnerFromDB(const pRunner db_r, int classId, bool autoAdd) { oRunner r(this); r.sName = db_r->sName; oRunner::getRealName(r.sName, r.tRealName); r.CardNo = db_r->CardNo; if (db_r->Club) { r.Club = getClub(db_r->getClubId()); if (!r.Club) r.Club = addClub(*db_r->Club); } r.Class=classId ? getClass(classId) : 0; memcpy(r.oData, db_r->oData, sizeof(r.oData)); pRunner pr = addRunner(r, true); if (pr->getDI().getInt("EntryDate") == 0) pr->getDI().setDate("EntryDate", getLocalDate()); if (r.Class) { int heat = r.Class->getDCI().getInt("Heat"); if (heat != 0) pr->getDI().setInt("Heat", heat); } pr->updateChanged(); if (autoAdd) autoAddTeam(pr); return pr; } pRunner oEvent::addRunner(const oRunner &r, bool updateStartNo) { bool needUpdate = Runners.empty(); Runners.push_back(r); pRunner pr=&Runners.back(); if (pr->StartNo == 0 && updateStartNo) { pr->StartNo = ++nextFreeStartNo; // Need not be unique } else { nextFreeStartNo = max(nextFreeStartNo, pr->StartNo); } if (pr->Card) pr->Card->tOwner = pr; if (HasDBConnection) { if (!pr->existInDB()) pr->synchronize(); } if (needUpdate) oe->updateTabs(); if (pr->Class) pr->Class->tResultInfo.clear(); bibStartNoToRunnerTeam.clear(); runnerById[pr->Id] = pr; // Notify runner database that runner has entered getRunnerDatabase().hasEnteredCompetition(r.getExtIdentifier()); return pr; } pRunner oEvent::addRunnerVacant(int classId) { pRunner r=addRunner(lang.tl("Vakant"), getVacantClub(false), classId, 0,0, true); if (r) { r->apply(false, 0, false); r->synchronize(true); } return r; } int oEvent::getFreeCourseId() { qFreeCourseId++; return qFreeCourseId; } int oEvent::getFreeControlId() { qFreeControlId++; return qFreeControlId; } string oEvent::getAutoCourseName() const { char bf[32]; sprintf_s(bf, lang.tl("Bana %d").c_str(), Courses.size()+1); return bf; } int oEvent::getFreeClassId() { qFreeClassId++; return qFreeClassId; } int oEvent::getFirstClassId(bool teamClass) const { for (oClassList::const_iterator it = Classes.begin(); it != Classes.end(); ++it) { if (it->isRemoved()) continue; int ns = it->getNumStages(); if (teamClass && ns > 0) return it->Id; else if (!teamClass && ns == 0) return it->Id; } return 0; } int oEvent::getFreeCardId() { qFreeCardId++; return qFreeCardId; } int oEvent::getFreePunchId() { qFreePunchId++; return qFreePunchId; } string oEvent::getAutoClassName() const { char bf[32]; sprintf_s(bf, 32, lang.tl("Klass %d").c_str(), Classes.size()+1); return bf; } string oEvent::getAutoTeamName() const { char bf[32]; sprintf_s(bf, 32, lang.tl("Lag %d").c_str(), Teams.size()+1); return bf; } string oEvent::getAutoRunnerName() const { char bf[32]; sprintf_s(bf, 32, lang.tl("Deltagare %d").c_str(), Runners.size()+1); return bf; } int oEvent::getFreeClubId() { qFreeClubId++; return qFreeClubId; } int oEvent::getFreeRunnerId() { qFreeRunnerId++; return qFreeRunnerId; } void oEvent::updateFreeId(oBase *obj) { if (typeid(*obj)==typeid(oRunner)){ qFreeRunnerId=max(obj->Id, qFreeRunnerId); } else if (typeid(*obj)==typeid(oClass)){ qFreeClassId=max(obj->Id, qFreeClassId); } else if (typeid(*obj)==typeid(oCourse)){ qFreeCourseId=max(obj->Id, qFreeCourseId); } else if (typeid(*obj)==typeid(oControl)){ qFreeControlId=max(obj->Id, qFreeControlId); } else if (typeid(*obj)==typeid(oClub)){ if (obj->Id != cVacantId && obj->Id != cNoClubId) qFreeClubId=max(obj->Id, qFreeClubId); } else if (typeid(*obj)==typeid(oCard)){ qFreeCardId=max(obj->Id, qFreeCardId); } else if (typeid(*obj)==typeid(oFreePunch)){ qFreePunchId=max(obj->Id, qFreePunchId); } else if (typeid(*obj)==typeid(oTeam)){ qFreeTeamId=max(obj->Id, qFreeTeamId); } /*else if (typeid(*obj)==typeid(oEvent)){ qFree }*/ } void oEvent::updateFreeId() { { oRunnerList::iterator it; qFreeRunnerId=0; nextFreeStartNo = 0; for (it=Runners.begin(); it != Runners.end(); ++it) { qFreeRunnerId = max(qFreeRunnerId, it->Id); nextFreeStartNo = max(nextFreeStartNo, it->StartNo); } } { oClassList::iterator it; qFreeClassId=0; for (it=Classes.begin(); it != Classes.end(); ++it) qFreeClassId=max(qFreeClassId, it->Id); } { oCourseList::iterator it; qFreeCourseId=0; for (it=Courses.begin(); it != Courses.end(); ++it) qFreeCourseId=max(qFreeCourseId, it->Id); } { oControlList::iterator it; qFreeControlId=0; for (it=Controls.begin(); it != Controls.end(); ++it) qFreeControlId=max(qFreeControlId, it->Id); } { oClubList::iterator it; qFreeClubId=0; for (it=Clubs.begin(); it != Clubs.end(); ++it) { if (it->Id != cVacantId && it->Id != cNoClubId) qFreeClubId=max(qFreeClubId, it->Id); } } { oCardList::iterator it; qFreeCardId=0; for (it=Cards.begin(); it != Cards.end(); ++it) qFreeCardId=max(qFreeCardId, it->Id); } { oFreePunchList::iterator it; qFreePunchId=0; for (it=punches.begin(); it != punches.end(); ++it) qFreePunchId=max(qFreePunchId, it->Id); } { oTeamList::iterator it; qFreeTeamId=0; for (it=Teams.begin(); it != Teams.end(); ++it) qFreeTeamId=max(qFreeTeamId, it->Id); } } int oEvent::getVacantClub(bool returnNoClubClub) { if (returnNoClubClub) { if (noClubId > 0) { pClub pc = getClub(noClubId); if (pc != 0 && !pc->isRemoved()) return noClubId; } pClub pc = getClub("Klubblös"); if (pc == 0) pc = getClub("No club"); //eng if (pc == 0) pc = getClub(lang.tl("Klubblös")); //other lang? if (pc == 0) pc=getClubCreate(cNoClubId, lang.tl("Klubblös")); noClubId = pc->getId(); return noClubId; } else { if (vacantId > 0) { pClub pc = getClub(vacantId); if (pc != 0 && !pc->isRemoved()) return vacantId; } pClub pc = getClub("Vakant"); if (pc == 0) pc = getClub("Vacant"); //eng if (pc == 0) pc = getClub(lang.tl("Vakant")); //other lang? if (pc == 0) pc=getClubCreate(cVacantId, lang.tl("Vakant")); vacantId = pc->getId(); return vacantId; } } int oEvent::getVacantClubIfExist(bool returnNoClubClub) const { if (returnNoClubClub) { if (noClubId > 0) { pClub pc = getClub(noClubId); if (pc != 0 && !pc->isRemoved()) return noClubId; } if (noClubId == -1) return 0; pClub pc=getClub("Klubblös"); if (pc == 0) pc = getClub("Klubblös"); if (pc == 0) pc = getClub(lang.tl("Klubblös")); //other lang? if (!pc) { noClubId = -1; return 0; } noClubId = pc->getId(); return noClubId; } else { if (vacantId > 0) { pClub pc = getClub(vacantId); if (pc != 0 && !pc->isRemoved()) return vacantId; } if (vacantId == -1) return 0; pClub pc=getClub("Vakant"); if (pc == 0) pc = getClub("Vacant"); if (pc == 0) pc = getClub(lang.tl("Vakant")); //other lang? if (!pc) { vacantId = -1; return 0; } vacantId = pc->getId(); return vacantId; } } pCard oEvent::allocateCard(pRunner owner) { oCard c(this); c.tOwner = owner; Cards.push_back(c); pCard newCard = &Cards.back(); return newCard; } bool oEvent::sortRunners(SortOrder so) { reinitializeClasses(); if (so == Custom) return false; CurrentSortOrder=so; Runners.sort(); return true; } bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg) { reinitializeClasses(); oTeamList::iterator it; map classId2Linear; if (so==ClassResult || so==ClassTotalResult || so == ClassTeamLegResult) { bool totalResult = so == ClassTotalResult; bool legResult = so == ClassTeamLegResult; bool hasRunner = (leg == -1); for (it=Teams.begin(); it != Teams.end(); ++it) { int lg = leg; int clsId = it->Class->getId(); if (leg>=0 && !linearLeg && it->Class) { map::iterator res = classId2Linear.find(it->Class->getId()); if (res == classId2Linear.end()) { unsigned linLegBase = it->Class->getLegNumberLinear(leg, 0); while (linLegBase + 1 < it->Class->getNumStages()) { if (it->Class->isParallel(linLegBase+1) || it->Class->isOptional(linLegBase+1)) linLegBase++; else break; } lg = linLegBase; //lg = linLegBase + it->Class->getNumParallel(linLegBase) - 1; classId2Linear[clsId] = lg; } else { lg = res->second; } } const int lastIndex = it->Class ? it->Class->getLastStageIndex() : 0; lg = min(lg, lastIndex); if (lg >= leg) hasRunner = true; if (legResult) { pRunner r = it->getRunner(lg); if (r) { it->_sortTime = r->getRunningTime(); it->_cachedStatus = r->getStatus(); } else { it->_sortTime = 0; it->_cachedStatus = StatusUnknown; } } else { it->_sortTime=it->getLegRunningTime(lg, totalResult) + it->getNumShortening(lg)*3600*24*10; it->_cachedStatus = it->getLegStatus(lg, totalResult); // Ensure number of restarts has effect on final result if (lg == lastIndex) it->_sortTime += it->tNumRestarts*24*3600; } unsigned rawStatus = it->_cachedStatus; it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0]; } if (!hasRunner) return false; Teams.sort(oTeam::compareResult); } else if (so==ClassStartTime) { for (it=Teams.begin(); it != Teams.end(); ++it) { it->_cachedStatus = StatusUnknown; it->_sortStatus=0; it->_sortTime=it->getLegStartTime(leg); if (it->_sortTime<=0) it->_sortStatus=1; } Teams.sort(oTeam::compareResult); } return true; } string oEvent::getZeroTime() const { return getAbsTime(0); } void oEvent::setZeroTime(string m) { unsigned nZeroTime = convertAbsoluteTime(m); if (nZeroTime!=ZeroTime && nZeroTime != -1) { updateChanged(); ZeroTime=nZeroTime; } } void oEvent::setName(const string &m) { if (trim(m).empty()) throw meosException("Tomt namn är inte tillåtet."); if (m != getName()) { Name = m; updateChanged(); } } void oEvent::setAnnotation(const string &m) { if (m!=Annotation) { Annotation=m; updateChanged(); } } string oEvent::getTitleName() const { if (empty()) return ""; if (HasPendingDBConnection) return getName() + lang.tl(" (på server)") + lang.tl(" DATABASE ERROR"); else if (isClient()) return getName() + lang.tl(" (på server)"); else return getName() + lang.tl(" (lokalt)"); } void oEvent::setDate(const string &m) { if (m!=Date) { int d = convertDateYMS(m, true); if (d <= 0) throw meosException("Felaktigt datumformat 'X' (Använd ÅÅÅÅ-MM-DD).#" + m); Date = formatDate(d, true); updateChanged(); } } const string &oEvent::getAbsTime(DWORD time) const { DWORD t = ZeroTime + time; if (int(t)<0) t = 0; int days = time/(3600*24); if (days <= 0) return formatTimeHMS(t % (24*3600)); else { string &res = StringCache::getInstance().get(); res = itos(days) + "D " + formatTimeHMS(t % (24*3600)); return res; } } const string &oEvent::getTimeZoneString() const { if (!date2LocalTZ.count(Date)) date2LocalTZ[Date] = ::getTimeZoneString(Date); return date2LocalTZ[Date]; } string oEvent::getAbsDateTimeISO(DWORD time, bool includeDate, bool useGMT) const { DWORD t = ZeroTime + time; string dateS, timeS; if (int(t)<0) { dateS = "2000-01-01"; if (useGMT) timeS = "00:00:00Z"; else timeS = "00:00:00" + getTimeZoneString(); } else { int extraDay; if (useGMT) { int offset = ::getTimeZoneInfo(Date); t += offset; if (t < 0) { extraDay = -1; t += 3600 * 24; } else { extraDay = t / (3600*24); } char bf[64]; sprintf_s(bf, "%02d:%02d:%02dZ", (t/3600)%24, (t/60)%60, t%60); timeS = bf; } else { char bf[64]; extraDay = t / (3600*24); sprintf_s(bf, "%02d:%02d:%02d", (t/3600)%24, (t/60)%60, t%60); timeS = bf + getTimeZoneString(); } if (extraDay == 0 ) { dateS = Date; } else { SYSTEMTIME st; convertDateYMS(Date, st, false); __int64 sec = SystemTimeToInt64Second(st); sec = sec + (extraDay * 3600 * 24); st = Int64SecondToSystemTime(sec); dateS = convertSystemDate(st); } } if (includeDate) return dateS + "T" + timeS; else return timeS; } const string &oEvent::getAbsTimeHM(DWORD time) const { DWORD t=ZeroTime+time; if (int(t)<0) return MakeDash("-"); char bf[32]; sprintf_s(bf, "%02d:%02d", (t/3600)%24, (t/60)%60); string &res = StringCache::getInstance().get(); res = bf; return res; } //Absolute time string to absolute time int (used by cvs-parser) int oEvent::convertAbsoluteTime(const string &m) { if (m.empty() || m[0]=='-') return -1; int len=m.length(); bool firstComma = false; for (int k=0;k='0' && b<='9')) ) { if (b==':' && firstComma == false) continue; else if ((b==',' || b=='.') && firstComma == false) { firstComma = true; continue; } return -1; } } int hour=atoi(m.c_str()); if (hour<0 || hour>23) return -1; int minute=0; int second=0; int kp=m.find_first_of(':'); if (kp>0) { string mtext=m.substr(kp+1); minute=atoi(mtext.c_str()); if (minute<0 || minute>60) minute=0; kp=mtext.find_last_of(':'); if (kp>0) { second=atoi(mtext.substr(kp+1).c_str()); if (second<0 || second>60) second=0; } } int t=hour*3600+minute*60+second; if (t<0) return 0; return t; } int oEvent::getRelativeTime(const string &date, const string &absoluteTime, const string &timeZone) const { int atime=convertAbsoluteTime(absoluteTime); if (timeZone == "Z" || timeZone == "z") { SYSTEMTIME st; convertDateYMS(date, st, false); st.wHour = atime / 3600; st.wMinute = (atime / 60) % 60; st.wSecond = atime % 60; SYSTEMTIME localTime; memset(&localTime, 0, sizeof(SYSTEMTIME)); SystemTimeToTzSpecificLocalTime(0, &st, &localTime); atime = localTime.wHour*3600 + localTime.wMinute * 60 + localTime.wSecond; } if (atime>=0 && atime<3600*24){ int rtime=atime-ZeroTime; if (rtime<=0) rtime+=3600*24; //Don't allow times just before zero time. if (rtime>3600*23) return -1; return rtime; } else return -1; } int oEvent::getRelativeTime(const string &m) const { int dayIndex = 0; for (size_t k = 0; k + 1 < m.length(); k++) { int c = m[k]; if (c == 'D' || c == 'd' || c == 'T' || c == 't') { dayIndex = k + 1; break; } } int atime; int days = 0; if (dayIndex == 0) atime = convertAbsoluteTime(m); else { atime = convertAbsoluteTime(m.substr(dayIndex)); days = atoi(m.c_str()); } if (atime>=0 && atime <= 3600*24){ int rtime = atime-ZeroTime; if (rtime < 0) rtime += 3600*24; rtime += days * 3600 * 24; //Don't allow times just before zero time. //if (rtime>3600*22) // return -1; return rtime; } else return -1; } int oEvent::getRelativeTimeFrom12Hour(const string &m) const { int atime=convertAbsoluteTime(m); if (atime>=0 && atime<3600*24) { int lowBound = ZeroTime; int highBound = ZeroTime + 3600 * 12; bool ok = ( atime >= lowBound && atime <= highBound) || ( (atime+3600*24) >= lowBound && (atime+3600*24) <= highBound); int rtime = atime - ZeroTime; if (!ok) rtime += 12 * 3600; rtime = (rtime+24*3600)%(24*3600); //int rtime=atime-(ZeroTime % (3600*12)); /* if (rtime<=0) rtime+=3600*12; */ //Don't allow times just before zero time. if (rtime>3600*22) return -1; return rtime; } else return -1; } void oEvent::removeRunner(const vector &ids) { oRunnerList::iterator it; set toRemove; for (size_t k = 0; k < ids.size(); k++) { int Id = ids[k]; pRunner r=getRunner(Id, 0); if (r==0) continue; r = r->tParentRunner ? r->tParentRunner : r; if (toRemove.count(r->getId())) continue; //Already found. //Remove a singe runner team autoRemoveTeam(r); for (size_t k=0;kmultiRunner.size();k++) if (r->multiRunner[k]) toRemove.insert(r->multiRunner[k]->getId()); toRemove.insert(Id); } if (toRemove.empty()) return; dataRevision++; set affectedCls; for (it=Runners.begin(); it != Runners.end();){ oRunner &cr = *it; if (toRemove.count(cr.getId())> 0) { if (cr.Class) affectedCls.insert(cr.Class); if (HasDBConnection) msRemove(&cr); toRemove.erase(cr.getId()); runnerById.erase(cr.getId()); if (cr.Card) { assert( cr.Card->tOwner == &cr ); cr.Card->tOwner = 0; } // Reset team runner (this should not happen) if (it->tInTeam) { if (it->tInTeam->Runners[it->tLeg]==&*it) it->tInTeam->Runners[it->tLeg] = 0; } oRunnerList::iterator next = it; ++next; Runners.erase(it); if (toRemove.empty()) { break; } else it = next; } else ++it; } for (set::iterator it = affectedCls.begin(); it != affectedCls.end(); ++it) { (*it)->clearCache(true); (*it)->markSQLChanged(-1,-1); } oe->updateTabs(); } void oEvent::removeCourse(int Id) { oCourseList::iterator it; for (it=Courses.begin(); it != Courses.end(); ++it){ if (it->Id==Id){ if (HasDBConnection) msRemove(&*it); dataRevision++; Courses.erase(it); courseIdIndex.erase(Id); return; } } } void oEvent::removeClass(int Id) { oClassList::iterator it; for (it=Classes.begin(); it != Classes.end(); ++it){ if (it->Id==Id){ if (HasDBConnection) msRemove(&*it); Classes.erase(it); dataRevision++; updateTabs(); return; } } } void oEvent::removeControl(int Id) { oControlList::iterator it; for (it=Controls.begin(); it != Controls.end(); ++it){ if (it->Id==Id){ if (HasDBConnection) msRemove(&*it); Controls.erase(it); dataRevision++; return; } } } void oEvent::removeClub(int Id) { oClubList::iterator it; for (it=Clubs.begin(); it != Clubs.end(); ++it){ if (it->Id==Id) { if (HasDBConnection) msRemove(&*it); Clubs.erase(it); clubIdIndex.erase(Id); dataRevision++; return; } } if (vacantId == Id) vacantId = 0; // Clear vacant id if (noClubId == Id) noClubId = 0; } void oEvent::removeCard(int Id) { oCardList::iterator it; for (it=Cards.begin(); it != Cards.end(); ++it) { if (it->getOwner() == 0 && it->Id == Id) { if (it->tOwner) { if (it->tOwner->Card == &*it) it->tOwner->Card = 0; } if (HasDBConnection) msRemove(&*it); Cards.erase(it); dataRevision++; return; } } } bool oEvent::isCourseUsed(int Id) const { oClassList::const_iterator it; for (it=Classes.begin(); it != Classes.end(); ++it){ if (it->isCourseUsed(Id)) return true; } oRunnerList::const_iterator rit; for (rit=Runners.begin(); rit != Runners.end(); ++rit){ pCourse pc=rit->getCourse(false); if (pc && pc->Id==Id) return true; } return false; } bool oEvent::isClassUsed(int Id) const { //Search runners oRunnerList::const_iterator it; for (it=Runners.begin(); it != Runners.end(); ++it){ if (it->getClassId()==Id) return true; } //Search teams oTeamList::const_iterator tit; for (tit=Teams.begin(); tit != Teams.end(); ++tit){ if (tit->getClassId()==Id) return true; } return false; } bool oEvent::isClubUsed(int Id) const { //Search runners oRunnerList::const_iterator it; for (it=Runners.begin(); it != Runners.end(); ++it){ if (it->getClubId()==Id) return true; } //Search teams oTeamList::const_iterator tit; for (tit=Teams.begin(); tit != Teams.end(); ++tit){ if (tit->getClubId()==Id) return true; } return false; } bool oEvent::isRunnerUsed(int Id) const { //Search teams oTeamList::const_iterator tit; for (tit=Teams.begin(); tit != Teams.end(); ++tit){ if (tit->isRunnerUsed(Id)) { if (tit->Class && tit->Class->isSingleRunnerMultiStage()) //Don't report single-runner-teams as blocking continue; return true; } } return false; } bool oEvent::isControlUsed(int Id) const { oCourseList::const_iterator it; for (it=Courses.begin(); it != Courses.end(); ++it){ for(int i=0;inControls;i++) if (it->Controls[i] && it->Controls[i]->Id==Id) return true; } return false; } bool oEvent::classHasResults(int Id) const { oRunnerList::const_iterator it; for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; if ( (Id == 0 || it->getClassId() == Id) && (it->getCard() || it->FinishTime)) return true; } return false; } bool oEvent::classHasTeams(int Id) const { oTeamList::const_iterator it; for (it=Teams.begin(); it != Teams.end(); ++it) if (it->getClassId()==Id) return true; return false; } void oEvent::generateVacancyList(gdioutput &gdi, GUICALLBACK cb) { sortRunners(ClassStartTime); oRunnerList::iterator it; // BIB, START, NAME, CLUB, SI int dx[5]={0, 0, 70, 150}; bool withbib=hasBib(true, false); int i; if (withbib) for (i=1;i<4;i++) dx[i]+=40; int y=gdi.getCY(); int x=gdi.getCX(); int lh=gdi.getLineHeight(); const int yStart = y; int nVac = 0; for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->skip() || !it->isVacant()) continue; nVac++; } int nCol = 1 + min(3, nVac/10); int RunnersPerCol = nVac / nCol; char bf[256]; int nRunner = 0; y+=lh; int Id=0; for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->skip() || !it->isVacant()) continue; if (it->getClassId() != Id) { Id=it->getClassId(); y+=lh/2; if (nRunner>=RunnersPerCol) { y = yStart; x += dx[3]+5; nRunner = 0; } gdi.addStringUT(y, x+dx[0], 1, it->getClass()); y+=lh+lh/3; } oDataInterface DI=it->getDI(); if (withbib) { string bib=it->getBib(); if (!bib.empty()) { gdi.addStringUT(y, x+dx[0], 0, bib); } } gdi.addStringUT(y, x+dx[1], 0, it->getStartTimeS(), 0, cb).setExtra(it->getId()); _itoa_s(it->Id, bf, 256, 10); gdi.addStringUT(y, x+dx[2], 0, it->getName(), dx[3]-dx[2]-4, cb).setExtra(it->getId()); //gdi.addStringUT(y, x+dx[3], 0, it->getClub()); y+=lh; nRunner++; } if (nVac==0) gdi.addString("", y, x, 0, "Inga vakanser tillgängliga. Vakanser skapas vanligen vid lottning."); gdi.updateScrollbars(); } void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb_nostart) { //Lazy setup: tie runners and persons oFreePunch::rehashPunches(*oe, 0, 0); // Map cardNo -> punch multimap punchHash; map cardCount; for (oRunnerList::const_iterator it = Runners.begin(); it != Runners.end(); ++it) { if (!it->isRemoved() && it->getCardNo() > 0) ++cardCount[it->getCardNo()]; } typedef multimap::const_iterator TPunchIter; for (oFreePunchList::iterator it = punches.begin(); it != punches.end(); ++it) { punchHash.insert(make_pair(it->getCardNo(), &*it)); } sortTeams(ClassStartTime, 0, true); int y=gdi.getCY(); int x=gdi.getCX(); int lh=gdi.getLineHeight(); oTeamList::iterator it; gdi.addStringUT(2, lang.tl("Kvar-i-skogen") + MakeDash(" - ") + getName()); y+=lh/2; gdi.addStringUT(1, getDate()); gdi.dropLine(); y+=3*lh; int id=0; int nr=0; for(it=Teams.begin(); it!=Teams.end(); ++it) { if (it->isRemoved()) continue; if (it->tStatus==StatusUnknown) { if (id != it->getClassId()) { if (nr>0) { gdi.addString("", y, x, 0, "Antal: X#"+itos(nr)); y+=lh; nr=0; } y += lh; id = it->getClassId(); gdi.addStringUT(y, x, 1, it->getClass()); y += lh; } gdi.addStringUT(y, x, 0, it->getClass()); nr++; gdi.addStringUT(y, x+100, 0, it->getName(), 0, cb).setExtra(it->getId()).id = "T"; y+=lh; } } if (nr>0) { gdi.addString("", y, x, 0, "Antal: X#"+itos(nr)); y+=lh; } { int tnr = 0; id=0; nr=0; sortRunners(ClassStartTime); oRunnerList::iterator it; int dx[4]={0, 70, 350, 470}; int y=gdi.getCY(); int x=gdi.getCX(); int lh=gdi.getLineHeight(); y+=lh; char bf[256]; y=gdi.getCY(); vector rr; setupCardHash(false); for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->skip() || it->needNoCard()) continue; if (it->tStatus == StatusUnknown) { if (id != it->getClassId()) { if (nr>0) { gdi.addString("", y, x, 0, "Antal: X#"+itos(nr)); y+=lh; nr=0; } y += lh; id = it->getClassId(); gdi.addStringUT(y, x, 1, it->getClass()); y += lh; } bool hasPunch = false; string punches; string otherRunners; pair range = punchHash.equal_range(it->getCardNo()); for (TPunchIter pit = range.first; pit != range.second; ++pit) { if (pit->second->tRunnerId == it->getId()) { if (hasPunch) punches.append(", "); else hasPunch = true; punches.append(pit->second->getSimpleString()); } } getRunnersByCardNo(it->getCardNo(), true, true, rr); for (size_t k = 0; k < rr.size(); k++) { if (rr[k]->getId() != it->getId()) { if (otherRunners.empty()) { otherRunners = lang.tl("Bricka X används också av: #" + itos(it->getCardNo())); } else { otherRunners += ", "; } otherRunners += rr[k]->getName(); } } gdi.addStringUT(y, x+dx[0], 0, it->getStartTimeS()); string club = it->getClub(); if (!club.empty()) club = " (" + club + ")"; gdi.addStringUT(y, x+dx[1], 0, it->getName()+club, dx[2]-dx[1]-4, cb).setExtra(it->getId()).id = "R"; _itoa_s(it->Id, bf, 256, 10); nr++; tnr++; if (hasPunch) { if (otherRunners.empty()) { RECT rc = gdi.addString("", y, x+dx[2], 0, "(har stämplat)", dx[3]-dx[2]-4).textRect; capitalize(punches); gdi.addToolTip("", "#" + punches, 0, &rc); } else { // Återanvänd bricka RECT rc = gdi.addString("", y, x+dx[2], 0, "#(" + lang.tl("reused card") + ")", dx[3]-dx[2]-4).textRect; capitalize(punches); gdi.addToolTip("", "#" + punches + ". " + otherRunners, 0, &rc); } } gdi.addStringUT(y, x+dx[3], 0, it->getClass()); y+=lh; } } if (nr>0) { gdi.addString("", y, x, 0, "Antal: X#"+itos(nr)); y+=lh; } if (tnr == 0 && Runners.size()>0) { gdi.addString("", 10, "inforestwarning"); } } setupCardHash(true); gdi.updateScrollbars(); } void oEvent::generateMinuteStartlist(gdioutput &gdi) { sortRunners(SortByStartTime); int dx[4]={0, gdi.scaleLength(70), gdi.scaleLength(340), gdi.scaleLength(510)}; int y=gdi.getCY(); int x=gdi.getCX(); int lh=gdi.getLineHeight(); vector blocks; vector starts; getStartBlocks(blocks, starts); char bf[256]; for (size_t k=0;k0) gdi.addStringUT(gdi.getCY()-1, 0, pageNewPage, ""); gdi.addStringUT(boldLarge, lang.tl("Minutstartlista") + MakeDash(" - ") + getName()); if (!starts[k].empty()) { sprintf_s(bf, lang.tl("%s, block: %d").c_str(), starts[k].c_str(), blocks[k]); gdi.addStringUT(fontMedium, bf); } else if (blocks[k]!=0) { sprintf_s(bf, lang.tl("Startblock: %d").c_str(), blocks[k]); gdi.addStringUT(fontMedium, bf); } vector< vector< vector > > sb; sb.reserve(Runners.size()); int LastStartTime=-1; for (oRunnerList::iterator it=Runners.begin(); it != Runners.end(); ++it) { if (it->Class && it->Class->getBlock()!=blocks[k]) continue; if (it->Class && it->Class->getStart() != starts[k] ) continue; if (!it->Class && blocks[k]!=0) continue; if (it->getStatus() == StatusNotCompetiting) continue; if (LastStartTime!=it->tStartTime) { sb.resize(sb.size() + 1); LastStartTime = it->tStartTime; } if (sb.empty()) sb.resize(1); if (it->tInTeam == 0) sb.back().push_back(vector(1, &*it)); else { if (it->legToRun() > 0 && it->getStartTime() == 0) continue; int minIx = 10000; for (int j = 0; j < it->tInTeam->getNumRunners(); j++) { if (j != it->tLeg && it->tInTeam->Runners[j] && it->tInTeam->Runners[j]->tStartTime == it->tStartTime) minIx = min(minIx, j); } if (minIx == 10000) sb.back().push_back(vector(1, &*it)); // Single runner on this start time else if (minIx > it->tLeg) { sb.back().push_back(vector()); for (int j = 0; j < it->tInTeam->getNumRunners(); j++) { if (it->tInTeam->Runners[j] && it->tInTeam->Runners[j]->tStartTime == it->tStartTime) sb.back().back().push_back(it->tInTeam->Runners[j]); } } } } y = gdi.getCY(); for (size_t k = 0; k < sb.size(); k++) { if (sb[k].empty()) continue; y+=lh/2; gdi.addStringUT(y, x+dx[0], boldText, sb[k][0][0]->getStartTimeS()); y+=lh; for (size_t j = 0; j < sb[k].size(); j++) { const int src_y = y; int indent = 0; const vector &r = sb[k][j]; if (r.size() == 1) { if (r[0]->getCardNo()>0) gdi.addStringUT(y, x+dx[0], fontMedium, itos(r[0]->getCardNo())); string name; if (r[0]->getBib().empty()) name = r[0]->getName(); else name = r[0]->getName() + " (" + r[0]->getBib() + ")"; gdi.addStringUT(y, x+dx[1], fontMedium, name, dx[2]-dx[1]-4); } else { string name; if (!r[0]->tInTeam->getBib().empty()) name = r[0]->tInTeam->getBib() + ": "; int nnames = 0; for (size_t i = 0; i < r.size(); i++) { if (nnames>0) name += ", "; nnames++; if (nnames > 2) { gdi.addStringUT(y, x+dx[0]+indent, fontMedium, name, dx[2]-dx[0]-4-indent); name.clear(); nnames = 1; y+=lh; indent = gdi.scaleLength(20); } name += r[i]->getName(); if (r[i]->getCardNo()>0) { name += " (" + itos(r[i]->getCardNo()) + ")"; } } gdi.addStringUT(y, x+dx[0]+indent, fontMedium, name, dx[2]-dx[0]-4-indent); } gdi.addStringUT(src_y, x+dx[2], fontMedium, r[0]->getClub(), dx[3]-dx[2]-4); gdi.addStringUT(src_y, x+dx[3], fontMedium, r[0]->getClass()); y+=lh; } } } gdi.refresh(); } const string &oEvent::getName() const { if (Name.size() > 1 && Name.at(0) == '%') { return lang.tl(Name.substr(1)); } else return Name; } bool oEvent::empty() const { return Name.empty(); } void oEvent::clearListedCmp() { cinfo.clear(); } bool oEvent::enumerateCompetitions(const char *file, const char *filetype) { WIN32_FIND_DATA fd; char dir[MAX_PATH]; char FullPath[MAX_PATH]; strcpy_s(dir, MAX_PATH, file); if (dir[strlen(file)-1]!='\\') strcat_s(dir, MAX_PATH, "\\"); strcpy_s(FullPath, MAX_PATH, dir); strcat_s(dir, MAX_PATH, filetype); HANDLE h=FindFirstFile(dir, &fd); if (h==INVALID_HANDLE_VALUE) return false; bool more=true; int id=1; cinfo.clear(); while (more) { if (fd.cFileName[0]!='.') //Avoid .. and . { char FullPathFile[MAX_PATH]; strcpy_s(FullPathFile, MAX_PATH, FullPath); strcat_s(FullPathFile, MAX_PATH, fd.cFileName); CompetitionInfo ci; ci.FullPath=FullPathFile; ci.Name=""; ci.Date="2007-01-01"; ci.Id=id++; SYSTEMTIME st; FileTimeToSystemTime(&fd.ftLastWriteTime, &st); ci.Modified=convertSystemTime(st); xmlparser xp(0); try { xp.read(FullPathFile, 8); const xmlobject date=xp.getObject("Date"); if (date) ci.Date=date.get(); const xmlobject name=xp.getObject("Name"); if (name) { ci.Name=name.get(); if (ci.Name.size() > 1 && ci.Name.at(0) == '%') { ci.Name = lang.tl(ci.Name.substr(1)); } } const xmlobject annotation=xp.getObject("Annotation"); if (annotation) ci.Annotation=annotation.get(); const xmlobject nameid = xp.getObject("NameId"); if (nameid) ci.NameId = nameid.get(); cinfo.push_front(ci); } catch (std::exception &) { // XXX Do what?? } } more=FindNextFile(h, &fd)!=0; } FindClose(h); if (!getServerName().empty()) msListCompetitions(this); for (list::iterator it=cinfo.begin(); it!=cinfo.end(); ++it) { if (it->Name.size() > 1 && it->Name[0] == '%') it->Name = lang.tl(it->Name.substr(1)); } return true; } bool oEvent::enumerateBackups(const char *file) { backupInfo.clear(); enumerateBackups(file, "*.meos.bu?", 1); enumerateBackups(file, "*.removed", 1); enumerateBackups(file, "*.dbmeos*", 2); backupInfo.sort(); int id = 1; for (list::iterator it = backupInfo.begin(); it != backupInfo.end(); ++it) { it->backupId = id++; } return true; } const BackupInfo &oEvent::getBackup(int bid) const { for (list::const_iterator it = backupInfo.begin(); it != backupInfo.end(); ++it) { if (it->backupId == bid) { return *it; } } throw meosException("Internal error"); } void oEvent::deleteBackups(const BackupInfo &bu) { string file = bu.fileName + bu.Name; list toRemove; for (list::iterator it = backupInfo.begin(); it != backupInfo.end(); ++it) { if (file == it->fileName + it->Name) toRemove.push_back(it->FullPath); } if (!toRemove.empty()) { char path[260]; char drive[48]; char filename[260]; char ext[64]; //_splitpath_s(toRemove.back().c_str(), drive, ds, path, dirs, filename, fns, ext, exts); _splitpath_s(toRemove.back().c_str(), drive, path, filename, ext); string dest = string(drive) + path; toRemove.push_back(dest + bu.fileName + ".persons"); toRemove.push_back(dest + bu.fileName + ".clubs"); for (list::iterator it = toRemove.begin(); it != toRemove.end(); ++it) { DeleteFile(it->c_str()); } } } bool oEvent::listBackups(gdioutput &gdi, GUICALLBACK cb) { int y = gdi.getCY(); int x = gdi.getCX(); list::iterator it = backupInfo.begin(); while (it != backupInfo.end()) { list::iterator sum_size = it; size_t s = 0; //string date = it->Modified; string file = it->fileName + it->Name; while(sum_size != backupInfo.end() && file == sum_size->fileName + sum_size->Name) { s += sum_size->fileSize; ++sum_size; } string type = lang.tl(it->type==1 ? "backup" : "serverbackup"); string size; if (s < 1024) { size = itos(s) + " bytes"; } else if (s < 1024*512) { size = itos(s/1024) + " kB"; } else { size = itos(s/(1024*1024)) + "." + itos( ((10*(s/1024))/1024)%10) + " MB"; } gdi.dropLine(); gdi.addStringUT(gdi.getCY(), gdi.getCX(), boldText, it->Name + " (" + it->Date + ") " + type, 400); gdi.pushX(); gdi.fillRight(); gdi.addString("", 0, "Utrymme: X#" + size); gdi.addString("EraseBackup", 0, "[Radera]", cb).setExtra(it->backupId); gdi.fillDown(); gdi.popX(); gdi.dropLine(1.5); y = gdi.getCY(); while(it != backupInfo.end() && file == it->fileName + it->Name) { gdi.addStringUT(y, x+30, 0, it->Modified, 400, cb).setExtra(it->backupId); ++it; y += gdi.getLineHeight(); } } return true; } bool BackupInfo::operator<(const BackupInfo &ci) { if (Date!=ci.Date) return Date>ci.Date; if (fileName!=ci.fileName) return fileNameci.Modified; } bool oEvent::enumerateBackups(const char *file, const char *filetype, int type) { WIN32_FIND_DATA fd; char dir[MAX_PATH]; char FullPath[MAX_PATH]; strcpy_s(dir, MAX_PATH, file); if (dir[strlen(file)-1]!='\\') strcat_s(dir, MAX_PATH, "\\"); strcpy_s(FullPath, MAX_PATH, dir); strcat_s(dir, MAX_PATH, filetype); HANDLE h=FindFirstFile(dir, &fd); if (h==INVALID_HANDLE_VALUE) return false; bool more=true; while (more) { if (fd.cFileName[0]!='.') {//Avoid .. and . char FullPathFile[MAX_PATH]; strcpy_s(FullPathFile, MAX_PATH, FullPath); strcat_s(FullPathFile, MAX_PATH, fd.cFileName); BackupInfo ci; ci.type = type; ci.FullPath=FullPathFile; ci.Name=""; ci.Date="2007-01-01"; ci.fileName = fd.cFileName; ci.fileSize = fd.nFileSizeLow; size_t pIndex = ci.fileName.find_first_of("."); if (pIndex>0 && pIndex 1 && ci.Name.at(0) == '%') { ci.Name = lang.tl(ci.Name.substr(1)); } } backupInfo.push_front(ci); } catch (std::exception &) { //XXX Do what? } } more=FindNextFile(h, &fd)!=0; } FindClose(h); return true; } bool oEvent::fillCompetitions(gdioutput &gdi, const string &name, int type, const string &select) { cinfo.sort(); cinfo.reverse(); list::iterator it; gdi.clearList(name); string b; int idSel = -1; //char bf[128]; for (it=cinfo.begin(); it!=cinfo.end(); ++it) { string annotation; if (!it->Annotation.empty()) annotation = " (" + it->Annotation + ")"; if (it->Server.length()==0) { if (type==0 || type==1) { if (it->NameId == select && !select.empty()) idSel = it->Id; string bf = "[" + it->Date + "] " + it->Name; gdi.addItem(name, bf + annotation, it->Id); } } else if (type==0 || type==2) { if (it->NameId == select && !select.empty()) idSel = it->Id; string bf; if (type==0) bf = lang.tl("Server: [X] Y#" + it->Date + "#" + it->Name); else bf = "[" + it->Date + "] " + it->Name; gdi.addItem(name, bf + annotation, 10000000+it->Id); } } if (idSel != -1) gdi.selectItemByData(name.c_str(), idSel); return true; } void tabAutoKillMachines(); void oEvent::checkDB() { if (HasDBConnection) { vector err; int k=checkChanged(err); #ifdef _DEBUG if (k>0) { char bf[256]; sprintf_s(bf, "Databasen innehåller %d osynkroniserade ändringar.", k); string msg(bf); for(int i=0;i < min(err.size(), 10);i++) msg+=string("\n")+err[i]; MessageBox(0, msg.c_str(), "Varning/Fel", MB_OK); } #endif } updateTabs(); gdibase.setWindowTitle(getTitleName()); } void destroyExtraWindows(); void oEvent::clear() { checkDB(); if (HasDBConnection) msMonitor(0); HasDBConnection=false; HasPendingDBConnection = false; destroyExtraWindows(); while (!tables.empty()) { tables.begin()->second->releaseOwnership(); tables.erase(tables.begin()); } Table::resetTableIds(); getRunnerDatabase().releaseTables(); getMeOSFeatures().clear(*this); Id=0; dataRevision = 0; tClubDataRevision = -1; ZeroTime=0; Name.clear(); Annotation.clear(); //Make sure no daemon is hunting us. tabAutoKillMachines(); delete directSocket; directSocket = 0; tLongTimesCached = -1; //Order of destruction is extreamly important... runnerById.clear(); bibStartNoToRunnerTeam.clear(); Runners.clear(); Teams.clear(); teamById.clear(); Classes.clear(); Courses.clear(); courseIdIndex.clear(); Controls.clear(); Cards.clear(); Clubs.clear(); clubIdIndex.clear(); punchIndex.clear(); punches.clear(); updateFreeId(); strcpy_s(CurrentNameId, ""); strcpy_s(CurrentFile, ""); sqlCounterRunners=0; sqlCounterClasses=0; sqlCounterCourses=0; sqlCounterControls=0; sqlCounterClubs=0; sqlCounterCards=0; sqlCounterPunches=0; sqlCounterTeams=0; sqlUpdateControls.clear(); sqlUpdateCards.clear(); sqlUpdateClubs.clear(); sqlUpdateClasses.clear(); sqlUpdateCourses.clear(); sqlUpdateRunners.clear(); sqlUpdatePunches.clear(); sqlUpdateTeams.clear(); vacantId = 0; noClubId = 0; oEventData->initData(this, sizeof(oData)); timelineClasses.clear(); timeLineEvents.clear(); nextTimeLineEvent = 0; tCurrencyFactor = 1; tCurrencySymbol = "kr"; tCurrencySeparator = ","; tCurrencyPreSymbol = false; readPunchHash.clear(); //Reset speaker data structures. listContainer->clearExternal(); while(!generalResults.empty() && generalResults.back().isDynamic()) generalResults.pop_back(); // Cleanup user interface gdibase.getTabs().clearCompetitionData(); MeOSUtil::useHourFormat = getPropertyInt("UseHourFormat", 1) != 0; currentNameMode = (NameMode) getPropertyInt("NameMode", FirstLast); } bool oEvent::deleteCompetition() { if (!empty() && !HasDBConnection) { string removed = string(CurrentFile)+".removed"; ::remove(removed.c_str()); //Delete old removed file openFileLock->unlockFile(); ::rename(CurrentFile, removed.c_str()); return true; } else return false; } void oEvent::newCompetition(const string &name) { openFileLock->unlockFile(); clear(); SYSTEMTIME st; GetLocalTime(&st); Date = convertSystemDate(st); ZeroTime = st.wHour*3600; Name = name; oEventData->initData(this, sizeof(oData)); getDI().setString("Organizer", getPropertyString("Organizer", "")); getDI().setString("Street", getPropertyString("Street", "")); getDI().setString("Address", getPropertyString("Address", "")); getDI().setString("EMail", getPropertyString("EMail", "")); getDI().setString("Homepage", getPropertyString("Homepage", "")); getDI().setInt("CardFee", getPropertyInt("CardFee", 25)); getDI().setInt("EliteFee", getPropertyInt("EliteFee", 130)); getDI().setInt("EntryFee", getPropertyInt("EntryFee", 90)); getDI().setInt("YouthFee", getPropertyInt("YouthFee", 50)); getDI().setInt("SeniorAge", getPropertyInt("SeniorAge", 0)); getDI().setInt("YouthAge", getPropertyInt("YouthAge", 16)); getDI().setString("Account", getPropertyString("Account", "")); getDI().setString("LateEntryFactor", getPropertyString("LateEntryFactor", "50 %")); getDI().setString("CurrencySymbol", getPropertyString("CurrencySymbol", "kr")); getDI().setString("CurrencySeparator", getPropertyString("CurrencySeparator", ".")); getDI().setInt("CurrencyFactor", getPropertyInt("CurrencyFactor", 1)); getDI().setInt("CurrencyPreSymbol", getPropertyInt("CurrencyPreSymbol", 0)); getDI().setString("PayModes", getPropertyString("PayModes", "")); setCurrency(-1, "", "", 0); char file[260]; char filename[64]; sprintf_s(filename, 64, "meos_%d%02d%02d_%02d%02d%02d_%X.meos", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); //strcpy_s(CurrentNameId, filename); getUserFile(file, filename); strcpy_s(CurrentFile, MAX_PATH, file); _splitpath_s(CurrentFile, NULL, 0, NULL,0, CurrentNameId, 64, NULL, 0); int i=0; while (CurrentNameId[i]) { if (CurrentNameId[i]=='.') { CurrentNameId[i]=0; break; } i++; } oe->updateTabs(); } void oEvent::reEvaluateCourse(int CourseId, bool DoSync) { oRunnerList::iterator it; if (DoSync) autoSynchronizeLists(false); vector mp; set classes; for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->getCourse(false) && it->getCourse(false)->getId()==CourseId){ classes.insert(it->getClassId()); } } reEvaluateAll(classes, DoSync); } void oEvent::reEvaluateAll(const set &cls, bool doSync) { if (doSync) autoSynchronizeLists(false); for(oClassList::iterator it=Classes.begin();it!=Classes.end();++it) { if (cls.empty() || cls.count(it->Id)) { it->clearSplitAnalysis(); it->resetLeaderTime(); it->reinitialize(); } } for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { if (!cls.empty() && cls.count(tit->getClassId()) == 0) continue; tit->resetTmpStore(); int nr = tit->getNumRunners(); for (int k = 0; k < nr; k++) { if (tit->Runners[k]) tit->Runners[k]->resetTmpStore(); } if (!tit->isRemoved()) { tit->apply(false, 0, true); } } oRunnerList::iterator it; for (it=Runners.begin(); it != Runners.end(); ++it) { if (!cls.empty() && cls.count(it->getClassId()) == 0) continue; if (!it->tInTeam) { it->resetTmpStore(); it->apply(false, 0, true); } } vector mp; bool needupdate = true; int leg = 0; while (needupdate) { needupdate = false; for (it=Runners.begin(); it != Runners.end(); ++it) { if (!cls.empty() && cls.count(it->getClassId()) == 0) continue; if (!it->isRemoved()) { if (it->tLeg == leg) { it->evaluateCard(false, mp); // Must not sync! it->storeTimes(); } else if (it->tLeg>leg) needupdate = true; } } leg++; } // Update team start times etc. for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { if (!tit->isRemoved()) { if (!cls.empty() && cls.count(tit->getClassId()) == 0) continue; tit->apply(false, 0, true); tit->setTmpStore(); if (doSync) tit->synchronize(true); } } for (it=Runners.begin(); it != Runners.end(); ++it) { if (!it->isRemoved()) { if (!cls.empty() && cls.count(it->getClassId()) == 0) continue; if (!it->tInTeam) it->apply(false, 0, true); it->setTmpStore(); it->storeTimes(); it->clearOnChangedRunningTime(); } } //reCalculateLeaderTimes(0); } void oEvent::reEvaluateChanged() { if (sqlChangedClasses || sqlChangedCourses || sqlChangedControls) { reEvaluateAll(set(), false); globalModification = true; return; } if (sqlChangedClubs) globalModification = true; if (!sqlChangedCards && !sqlChangedRunners && !sqlChangedTeams) return; // Nothing to do map resetClasses; for(oClassList::iterator it=Classes.begin();it!=Classes.end();++it) { if (it->wasSQLChanged(-1, oPunch::PunchFinish)) { it->clearSplitAnalysis(); it->resetLeaderTime(); it->reinitialize(); resetClasses[it->getId()] = it->hasClassGlobalDependance(); } } stdext::hash_set addedTeams; for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { if (tit->isRemoved() || !tit->wasSQLChanged()) continue; addedTeams.insert(tit->getId()); tit->resetTmpStore(); int nr = tit->getNumRunners(); for (int k = 0; k < nr; k++) { if (tit->Runners[k]) tit->Runners[k]->resetTmpStore(); } tit->apply(false, 0, true); } oRunnerList::iterator it; vector< vector > legRunners(maxRunnersTeam); if (Teams.size() > 0) { for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; if (resetClasses.count(it->getClassId())) it->storeTimes(); if (!it->wasSQLChanged() && !resetClasses[it->getClassId()]) continue; pTeam t = it->tInTeam; if (t && !addedTeams.count(t->getId())) { addedTeams.insert(t->getId()); t->resetTmpStore(); int nr = t->getNumRunners(); for (int k = 0; k < nr; k++) { if (t->Runners[k]) t->Runners[k]->resetTmpStore(); } t->apply(false, 0, true); } } } for (it=Runners.begin(); it != Runners.end(); ++it) { pRunner r = &*it; if (r->isRemoved()) continue; if (r->wasSQLChanged() || (r->tInTeam && addedTeams.count(r->tInTeam->getId()))) { unsigned leg = r->tLeg; if (leg <0 || leg >= maxRunnersTeam) leg = 0; if (legRunners[leg].empty()) legRunners[leg].reserve(Runners.size() / (leg+1)); legRunners[leg].push_back(r); if (!r->tInTeam) { r->resetTmpStore(); r->apply(false, 0, true); } } else { if (r->Class && r->Class->wasSQLChanged(-1, oPunch::PunchFinish)) { it->storeTimes(); } } } vector mp; // Reevaluate for (size_t leg = 0; leg < legRunners.size(); leg++) { const vector &lr = legRunners[leg]; for (size_t k = 0; k < lr.size(); k++) { lr[k]->evaluateCard(false, mp); // Must not sync! } } for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { if (addedTeams.count(tit->getId())) { tit->apply(false, 0, true); tit->setTmpStore(); } } for (size_t leg = 0; leg < legRunners.size(); leg++) { const vector &lr = legRunners[leg]; for (size_t k = 0; k < lr.size(); k++) { if (!lr[k]->tInTeam) lr[k]->apply(false, 0, true); lr[k]->setTmpStore(); lr[k]->clearOnChangedRunningTime(); } } } void oEvent::reCalculateLeaderTimes(int classId) { if (disableRecalculate) return; #ifdef _DEBUG char bf[128]; sprintf_s(bf, "Calculate leader times %d\n", classId); OutputDebugString(bf); #endif for (oClassList::iterator it=Classes.begin(); it != Classes.end(); ++it) { if (!it->isRemoved() && (classId==it->getId() || classId==0)) it->resetLeaderTime(); } bool needupdate = true; int leg = 0; while (needupdate) { needupdate = false; for (oRunnerList::iterator it=Runners.begin(); it != Runners.end(); ++it) { if (!it->isRemoved() && (classId==0 || classId==it->getClassId())) { if (it->tLeg == leg) it->storeTimes(); else if (it->tLeg>leg) needupdate = true; } } leg++; } } string oEvent::getCurrentTimeS() const { SYSTEMTIME st; GetLocalTime(&st); char bf[64]; sprintf_s(bf, 64, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); return bf; } int oEvent::findBestClass(const SICard &card, vector &classes) const { classes.clear(); int Distance=-1000; oClassList::const_iterator it; for (it=Classes.begin(); it != Classes.end(); ++it) { vector courses; it->getCourses(0, courses); bool insertClass = false; // Make sure a class is only included once for (size_t k = 0; kdistance(card); if (d>=0) { if (Distance<0) Distance=1000; if (dDistance) { Distance = d; classes.clear(); insertClass = true; classes.push_back(pClass(&*it)); } else if (Distance == d) { if (!insertClass) { insertClass = true; classes.push_back(pClass(&*it)); } } } } } } return Distance; } void oEvent::convertTimes(SICard &sic) const { if (sic.convertedTime) return; sic.convertedTime = true; if (sic.CheckPunch.Code!=-1){ if (sic.CheckPunch.Time 0) { const int START = 1000; const int FINISH = 1001; vector > times; if (sic.StartPunch.Code!=-1) { if (sic.StartPunch.Time != -1) times.push_back(make_pair(sic.StartPunch.Time, START)); } for (unsigned k=0; k getClassId()==ClassId) if (it->tStartTimetStatus==StatusOK && it->tStartTime!=0) MinTime=it->tStartTime; ++it; } if (MinTime==3600*24) MinTime=0; return MinTime; } bool oEvent::exportONattResults(gdioutput &gdi, const string &file) { ofstream fout(file.c_str()); calculateResults(RTClassResult); sortRunners(ClassFinishTime); oRunnerList::iterator it; int Id=0; int ClassNr=1; bool warn=false; char bf[256]; int FirstStart=0; for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->tStatus!=0 && it->tStatus!=StatusDNS){ if (it->getClassId()!=Id){ //Next class Id=it->getClassId(); FirstStart=getFirstStart(Id); if (it->getClass()=="Lång") ClassNr=1; else if (it->getClass()=="Mellan") ClassNr=2; else if (it->getClass()=="Kort") ClassNr=3; else{ if (!warn) gdi.alert("Varning: Funktionen anpassad för klassnamn Lång, Mellan och Kort"); warn=true; ClassNr=Id; } } fout << ClassNr << ";"; int st=it->tStartTime-FirstStart; sprintf_s(bf, 64, "%d.%02d;", st/60, st%60); fout << bf; if (it->getStatus()==StatusOK){ int ft=it->FinishTime-FirstStart; sprintf_s(bf, 64, "%d.%02d;", ft/60, ft%60); fout << bf; } else fout << ";"; fout << it->getName() << ";"; fout << it->getClub() << endl; } } return true; } bool oEvent::hasRank() const { oRunnerList::const_iterator it; for (it=Runners.begin(); it != Runners.end(); ++it){ if (it->getDCI().getInt("Rank")>0) return true; } return false; } void oEvent::setMaximalTime(const string &t) { getDI().setInt("MaxTime", convertAbsoluteTime(t)); } int oEvent::getMaximalTime() const { return getDCI().getInt("MaxTime"); } string oEvent::getMaximalTimeS() const { return formatTime(getMaximalTime()); } bool oEvent::hasBib(bool runnerBib, bool teamBib) const { if (runnerBib) { oRunnerList::const_iterator it; for (it=Runners.begin(); it != Runners.end(); ++it){ if (!it->getBib().empty()) return true; } } if (teamBib) { oTeamList::const_iterator it; for (it=Teams.begin(); it != Teams.end(); ++it){ if (!it->getBib().empty()) return true; } } return false; } bool oEvent::hasTeam() const { return Teams.size() > 0; } void oEvent::addBib(int ClassId, int leg, const string &firstNumber) { if ( !classHasTeams(ClassId) ) { sortRunners(ClassStartTimeClub); oRunnerList::iterator it; if (!firstNumber.empty()) { char pattern[32]; int num = oClass::extractBibPattern(firstNumber, pattern); for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; if ( (ClassId==0 || it->getClassId()==ClassId) && it->legToRun()==leg) { char bib[32]; sprintf_s(bib, pattern, num); it->setBib(bib, num, true, false); num++; it->synchronize(); } } } else { for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->isRemoved()) continue; if (ClassId==0 || it->getClassId()==ClassId) { it->getDI().setString("Bib", "");//Update only bib it->synchronize(); } } } } else { oTeamList::iterator it; for (it=Teams.begin(); it != Teams.end(); ++it) it->apply(false, 0, false); if (!firstNumber.empty()) { // Clear out start number temporarily, to not use it for sorting for (it=Teams.begin(); it != Teams.end(); ++it) { if (ClassId==0 || it->getClassId()==ClassId) { if (it->getClassRef() && it->getClassRef()->getBibMode() != BibFree) { for (size_t i = 0; i < it->Runners.size(); i++) { if (it->Runners[i]) { it->Runners[i]->setStartNo(0, false); it->Runners[i]->setBib("", 0, false, false); } } } it->setStartNo(0, false); } } } sortTeams(ClassStartTime, 0, true); // Sort on first leg starttime and sortindex if (!firstNumber.empty()) { char pattern[32]; int num = oClass::extractBibPattern(firstNumber, pattern); for (it=Teams.begin(); it != Teams.end(); ++it) { if (ClassId==0 || it->getClassId()==ClassId) { char bib[32]; sprintf_s(bib, pattern, num); it->setBib(bib, num, true, false); num++; it->apply(true, 0, false); } } } else { for(it=Teams.begin(); it != Teams.end(); ++it){ if (ClassId==0 || it->getClassId()==ClassId) { it->getDI().setString("Bib", ""); //Update only bib it->apply(true, 0, false); } } } } } void oEvent::addAutoBib() { sortRunners(ClassStartTimeClub); oRunnerList::iterator it; int clsId = -1; int bibGap = oe->getBibClassGap(); int interval = 1; char pattern[32] = {0}; char storedPattern[32]; strcpy_s(storedPattern, "%d"); int number = 0; for (oTeamList::iterator tit=Teams.begin(); tit != Teams.end(); ++tit) { if (!tit->skip()) tit->apply(false, 0, false); } // Clear out start number temporarily, to not use it for sorting for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { if (tit->skip()) continue; pClass cls = tit->getClassRef(); if (cls == 0) continue; string bibInfo = cls->getDCI().getString("Bib"); bool teamAssign = !bibInfo.empty(); bool freeMode = cls->getBibMode()==BibFree; if (!teamAssign && freeMode) continue; // Manul or none bool addBib = bibInfo != "-"; if (addBib && teamAssign) tit->setStartNo(0, false); if (tit->getClassRef() && tit->getClassRef()->getBibMode() != BibFree) { for (size_t i = 0; i < tit->Runners.size(); i++) { if (tit->Runners[i]) { if (addBib && teamAssign) tit->Runners[i]->setStartNo(0, false); if (!freeMode) tit->Runners[i]->setBib("", 0, false, false); } } } } sortTeams(ClassStartTime, 0, true); // Sort on first leg starttime and sortindex map > cls2TeamList; for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { if (tit->skip()) continue; int clsId = tit->getClassId(); cls2TeamList[clsId].push_back(&*tit); } map > cls2RunnerList; for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->skip() || !it->getClassId()) continue; int clsId = it->getClassId(); cls2RunnerList[clsId].push_back(&*it); } Classes.sort(); for (oClassList::iterator clsIt = Classes.begin(); clsIt != Classes.end(); ++clsIt) { const pClass cls = &*clsIt; clsId = cls->getId(); string bibInfo = cls->getDCI().getString("Bib"); if (bibInfo.empty()) { // Skip class continue; } else if (bibInfo == "*") { if (number == 0) number = 1; else number += bibGap; if (pattern[0] == 0) { strcpy_s(pattern, storedPattern); } } else if (bibInfo == "-") { if (pattern[0]) { strcpy_s(storedPattern, pattern); } pattern[0] = 0; // Clear bibs in class } else { number = oClass::extractBibPattern(bibInfo, pattern); } if (cls->getNumStages() > 1) { vector &tl = cls2TeamList[clsId]; if (cls->getBibMode() == BibAdd) { int ns = cls->getNumStages(); if (ns <= 10) interval = 10; else interval = 100; if (bibInfo == "*") { int add = interval - number % interval; number += add; } } else { interval = 1; } if (pattern[0] == 0) { // Remove bib for (size_t k = 0; k < tl.size(); k++) { tl[k]->getDI().setString("Bib", ""); //Update only bib tl[k]->apply(true, 0, false); } } else { for (size_t k = 0; k < tl.size(); k++) { char buff[32]; sprintf_s(buff, pattern, number); tl[k]->setBib(buff, number, true, false); number += interval; tl[k]->apply(true, 0, false); } } continue; } else { interval = 1; vector &rl = cls2RunnerList[clsId]; for (size_t k = 0; k < rl.size(); k++) { if (pattern[0]) { char buff[32]; sprintf_s(buff, pattern, number); rl[k]->setBib(buff, number, true, false); number += interval; } else { rl[k]->getDI().setString("Bib", ""); //Update only bib } rl[k]->synchronize(); } } } } void oEvent::checkOrderIdMultipleCourses(int ClassId) { sortRunners(ClassStartTime); int order=1; oRunnerList::iterator it; //Find first free order for(it=Runners.begin(); it != Runners.end(); ++it){ if (ClassId==0 || it->getClassId()==ClassId){ it->synchronize();//Ensure we are up-to-date order=max(order, it->StartNo); } } //Assign orders for(it=Runners.begin(); it != Runners.end(); ++it){ if (ClassId==0 || it->getClassId()==ClassId) if (it->StartNo==0){ it->StartNo=++order; it->updateChanged(); //Mark as changed. it->synchronize(); //Sync! } } } void oEvent::fillStatus(gdioutput &gdi, const string& id) { vector< pair > d; oe->fillStatus(d); gdi.addItem(id, d); } const vector< pair > &oEvent::fillStatus(vector< pair > &out) { out.clear(); out.push_back(make_pair(lang.tl("-"), StatusUnknown)); out.push_back(make_pair(lang.tl("Godkänd"), StatusOK)); out.push_back(make_pair(lang.tl("Ej start"), StatusDNS)); out.push_back(make_pair(lang.tl("Felst."), StatusMP)); out.push_back(make_pair(lang.tl("Utg."), StatusDNF)); out.push_back(make_pair(lang.tl("Disk."), StatusDQ)); out.push_back(make_pair(lang.tl("Maxtid"), StatusMAX)); out.push_back(make_pair(lang.tl("Deltar ej"), StatusNotCompetiting)); return out; } int oEvent::getPropertyInt(const char *name, int def) { if (eventProperties.count(name)==1) return atoi(eventProperties[name].c_str()); else { setProperty(name, def); return def; } } const string &oEvent::getPropertyString(const char *name, const string &def) { if (eventProperties.count(name)==1) return eventProperties[name]; else { eventProperties[name] = def; return eventProperties[name]; } } string oEvent::getPropertyStringDecrypt(const char *name, const string &def) { char bf[MAX_COMPUTERNAME_LENGTH + 1]; DWORD len = MAX_COMPUTERNAME_LENGTH + 1; GetComputerName(bf, &len); string prop = getPropertyString(name, def); string prop2; int code = 0; const int s = 337; for (size_t j = 0; j>4) + 33; prop2.push_back((unsigned char)b1); prop2.push_back((unsigned char)b2); } setProperty(name, prop2); } void oEvent::setProperty(const char *name, int prop) { eventProperties[name]=itos(prop); } void oEvent::setProperty(const char *name, const string &prop) { eventProperties[name]=prop; } void oEvent::saveProperties(const char *file) { map::const_iterator it; xmlparser xml(0); xml.openOutputT(file, false, "MeOSPreference"); for (it = eventProperties.begin(); it != eventProperties.end(); ++it) { xml.write(it->first.c_str(), it->second); } xml.closeOut(); } void oEvent::loadProperties(const char *file) { eventProperties.clear(); initProperties(); try { xmlparser xml(0); xml.read(file); xmlobject xo = xml.getObject("MeOSPreference"); if (xo) { xmlList list; xo.getObjects(list); for (size_t k = 0; kgetStartNo() < b.tInTeam->getStartNo(); else return false; } return b.tInTeam!=0; } else return a.getClass()skip() || it->getCardNo() || it->isVacant() || it->needNoCard()) continue; if (it->getStatus() == StatusDNS || it->getStatus() == StatusNotCompetiting) continue; if (it->Club!=lastClub) { lastClub=it->Club; gdi.dropLine(0.5); gdi.addString("", 1, it->getClub()); } string r; if (it->Class) r+=it->getClass()+", "; if (it->tInTeam) { char bf[1024]; sprintf_s(bf, "%d %s, ", it->tInTeam->getStartNo(), it->tInTeam->getName().c_str()); r+=bf; } r+=it->getName()+":"; gdi.fillRight(); gdi.pushX(); gdi.addStringUT(0, r); char id[24]; sprintf_s(id, "*%d", k++); gdi.addInput(max(gdi.getCX(), 450), gdi.getCY()-4, id, "", 10, cb).setExtra(it->getId()); gdi.popX(); gdi.dropLine(1.6); gdi.fillDown(); } if (k==0) gdi.addString("", 0, "Ingen löpare saknar bricka"); } void oEvent::calcUseStartSeconds() { tUseStartSeconds=false; oRunnerList::iterator it; for (it=Runners.begin(); it != Runners.end(); ++it) if ( it->getStartTime()>0 && (it->getStartTime()+ZeroTime)%60!=0 ) { tUseStartSeconds=true; return; } } const string &oEvent::formatStatus(RunnerStatus status) { const static string stats[8]={"?", "Godkänd", "Ej start", "Felst.", "Utg.", "Disk.", "Maxtid", "Deltar ej"}; switch(status) { case StatusOK: return lang.tl(stats[1]); case StatusDNS: return lang.tl(stats[2]); case StatusMP: return lang.tl(stats[3]); case StatusDNF: return lang.tl(stats[4]); case StatusDQ: return lang.tl(stats[5]); case StatusMAX: return lang.tl(stats[6]); case StatusNotCompetiting: return lang.tl(stats[7]); default: return stats[0]; } } #ifndef MEOSDB void oEvent::analyzeClassResultStatus() const { map res; for (oRunnerList::const_iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved() || !it->Class) continue; int id = it->Class->Id * 31 + it->tLeg; ClassResultInfo &cri = res[id]; if (it->getStatus() == StatusUnknown) { cri.nUnknown++; if (it->tStartTime > 0) { if (!it->isVacant()) { if (cri.lastStartTime>=0) cri.lastStartTime = max(cri.lastStartTime, it->tStartTime); } } else cri.lastStartTime = -1; // Cannot determine } else cri.nFinished++; } for (oClassList::const_iterator it = Classes.begin(); it != Classes.end(); ++it) { if (it->isRemoved()) continue; if (!it->legInfo.empty()) { it->tResultInfo.resize(it->legInfo.size()); for (size_t k = 0; klegInfo.size(); k++) { int id = it->Id * 31 + k; it->tResultInfo[k] = res[id]; } } else { it->tResultInfo.resize(1); it->tResultInfo[0] = res[it->Id * 31]; } } } void oEvent::generateTestCard(SICard &sic) const { sic.clear(0); if (Runners.empty()) return; analyzeClassResultStatus(); oRunnerList::const_iterator it; int rNo = rand()%Runners.size(); it=Runners.begin(); while(rNo-->0) ++it; oRunner *r = 0; int cardNo = 0; while(r==0 && it!=Runners.end()) { cardNo = it->CardNo; if (cardNo == 0 && it->tParentRunner) cardNo = it->tParentRunner->CardNo; if (it->Class && it->tLeg>0) { StartTypes st = it->Class->getStartType(it->tLeg); if (st == STHunting) { if (it->Class->tResultInfo[it->tLeg-1].nUnknown > 0) cardNo = 0; // Wait with this leg } } // Make sure teams start in right order if (it->tInTeam && it->tLeg>0) { if (it->Class) { StartTypes st = it->Class->getStartType(it->tLeg); if (st != STDrawn && st != STTime) { pRunner prev = it->tInTeam->Runners[it->tLeg - 1]; if (prev && prev->getStatus() == StatusUnknown) cardNo = 0; // Wait with this runner } } } if (cardNo && !it->Card) { // For team runners, we require start time to get right order if (!it->tInTeam || it->tStartTime>0) r=pRunner(&*it); } ++it; } --it; while(r==0 && it!=Runners.begin()) { cardNo = it->CardNo; if (cardNo == 0 && it->tParentRunner) cardNo = it->tParentRunner->CardNo; if (it->Class && it->tLeg>0) { StartTypes st = it->Class->getStartType(it->tLeg); if (st == STHunting) { if (it->Class->tResultInfo[it->tLeg-1].nUnknown > 0) cardNo = 0; // Wait with this leg } } // Make sure teams start in right order if (it->tInTeam && it->tLeg>0) { if (it->Class) { StartTypes st = it->Class->getStartType(it->tLeg); if (st != STDrawn && st != STTime) { pRunner prev = it->tInTeam->Runners[it->tLeg - 1]; if (prev && prev->getStatus() == StatusUnknown) cardNo = 0; // Wait with this runner } } } if (cardNo && !it->Card) { // For team runners, we require start time to get right order if (!it->tInTeam || it->tStartTime>0) { r=pRunner(&*it); } } --it; } if (r) { r->synchronize(); pCourse pc=r->getCourse(false); if (!pc) { pClass cls = r->Class; if (cls) { pc = const_cast(this)->generateTestCourse(rand()%15+7); pc->synchronize(); cls->setCourse(pc); cls->synchronize(); } } if (pc) { sic.CardNumber = cardNo; if (rand()%5 == 3) sic.CardNumber = 100000; int s = sic.StartPunch.Time = r->tStartTime>0 ? r->tStartTime+ZeroTime : ZeroTime+3600+rand()%(3600*3); int tomiss = rand()%(60*10); if (tomiss>60*9) tomiss = rand()%30; else if (rand()%20 == 3) tomiss *= rand()%3; int f = sic.FinishPunch.Time = s+(30+pc->getLength()/200)*60+ rand()%(60*10) + tomiss; if (rand()%40==0 || r->tStartTime>0) sic.StartPunch.Code=-1; if (rand()%50==31) sic.FinishPunch.Code=-1; if (rand()%70==31) sic.CardNumber++; sic.nPunch=0; double dt=1./double(pc->nControls+1); int missed = 0; for(int k=0;knControls;k++) { if (rand()%130!=50) { sic.Punch[sic.nPunch].Code=pc->getControl(k)->Numbers[0]; double cc=(k+1)*dt; if (missed < tomiss) { int left = pc->nControls - k; if (rand() % left == 1) missed += ( (tomiss - missed) * (rand()%4 + 1))/6; else if (left == 1) missed = tomiss; } sic.Punch[sic.nPunch].Time=int((f-tomiss)*cc+s*(1.-cc)) + missed; sic.nPunch++; } } } } } pCourse oEvent::generateTestCourse(int nCtrl) { char bf[64]; static int sk=0; sprintf_s(bf, lang.tl("Bana %d").c_str(), ++sk); pCourse pc=addCourse(bf, 4000+(rand()%1000)*10); int i=0; for (;iaddControl(rand()%(99-32)+32); i++; pc->addControl(50)->setName("Radio 1"); for (;i<(2*nCtrl)/3;i++) pc->addControl(rand()%(99-32)+32); i++; pc->addControl(150)->setName("Radio 2"); for (;iaddControl(rand()%(99-32)+32); pc->addControl(100)->setName("Förvarning"); return pc; } pClass oEvent::generateTestClass(int nlegs, int nrunners, char *name, const string &start) { pClass cls=addClass(name); if (nlegs==1 && nrunners==1) { int nCtrl=rand()%15+5; if (rand()%10==1) nCtrl+=rand()%40; cls->setCourse(generateTestCourse(nCtrl)); } else if (nlegs==1 && nrunners==2) { setupRelay(*cls, PPatrol, 2, start); int nCtrl=rand()%15+10; pCourse pc=generateTestCourse(nCtrl); cls->addStageCourse(0, pc->getId()); cls->addStageCourse(1, pc->getId()); } else if (nlegs>1 && nrunners==2) { setupRelay(*cls, PTwinRelay, nlegs, start); int nCtrl=rand()%8+10; int cid[64]; for (int k=0;kgetId(); for (int k=0;kaddStageCourse(k, cid[(k+j)%nlegs]); } else if (nlegs>1 && nrunners==nlegs) { setupRelay(*cls, PRelay, nlegs, start); int nCtrl=rand()%8+10; int cid[64]; for (int k=0;kgetId(); for (int k=0;kaddStageCourse(k, cid[(k+j)%nlegs]); } else if (nlegs>1 && nrunners==1) { setupRelay(*cls, PHunting, 2, start); cls->addStageCourse(0, generateTestCourse(rand()%8+10)->getId()); cls->addStageCourse(1, generateTestCourse(rand()%8+10)->getId()); } return cls; } void oEvent::generateTestCompetition(int nClasses, int nRunners, bool generateTeams) { if (nClasses > 0) { oe->newCompetition("!TESTTÄVLING"); oe->setZeroTime("05:00:00"); oe->getMeOSFeatures().useAll(*oe); } bool useSOFTMethod = true; vector gname; //gname.reserve(RunnerDatabase.size()); vector fname; //fname.reserve(RunnerDatabase.size()); runnerDB->getAllNames(gname, fname); if (fname.empty()) fname.push_back("Foo"); if (gname.empty()) gname.push_back("Bar"); /* oRunnerList::iterator it; for(it=RunnerDatabase.begin(); it!=RunnerDatabase.end(); ++it){ if (!it->getGivenName().empty()) gname.push_back(it->getGivenName()); if (!it->getFamilyName().empty()) fname.push_back(it->getFamilyName()); } */ int nClubs=30; char bf[128]; int startno=1; const vector &oc = runnerDB->getClubDB(); for(int k=0;k2 && nRInClass>3) nRInClass+=int(nRInClass*0.7)-rand()%int(nRInClass*1.5); if (cls->getNumDistinctRunners()==1) { for (int i=0;igetId(), 0, 0, true); r->setStartNo(startno++, false); r->setCardNo(500001+Runners.size()*97+rand()%97, false); r->apply(false, 0, false); } nRunners-=nRInClass; if (k%5!=5) { vector spec; spec.push_back(ClassDrawSpecification(cls->getId(), 0, getRelativeTime(start), 10, 3)); drawList(spec, useSOFTMethod, 1, drawAll); } else cls->Name += " Öppen"; } else { int dr=cls->getNumDistinctRunners(); for (int i=0;igetId()); t->setStartNo(startno++, false); for (int j=0;jsetCardNo(500001+Runners.size()*97+rand()%97, false); t->setRunner(j, r, false); } } nRunners-=nRInClass; if ( cls->getStartType(0)==STDrawn ) { vector spec; spec.push_back(ClassDrawSpecification(cls->getId(), 0, getRelativeTime(start), 20, 3)); drawList(spec, useSOFTMethod, 1, drawAll); } } } } #endif void oEvent::getFreeImporter(oFreeImport &fi) { if (!fi.isLoaded()) fi.load(); fi.init(Runners, Clubs, Classes); } void oEvent::fillFees(gdioutput &gdi, const string &name, bool withAuto) const { gdi.clearList(name); set fees; int f; for (oClassList::const_iterator it = Classes.begin(); it != Classes.end(); ++it) { if (it->isRemoved()) continue; f = it->getDCI().getInt("ClassFee"); if (f > 0) fees.insert(f); f = it->getDCI().getInt("ClassFeeRed"); if (f > 0) fees.insert(f); if (withAuto) { f = it->getDCI().getInt("HighClassFee"); if (f > 0) fees.insert(f); f = it->getDCI().getInt("HighClassFeeRed"); if (f > 0) fees.insert(f); } } f = getDCI().getInt("EliteFee"); if (f > 0) fees.insert(f); f = getDCI().getInt("EntryFee"); if (f > 0) fees.insert(f); f = getDCI().getInt("YouthFee"); if (f > 0) fees.insert(f); vector< pair > ff; if (withAuto) ff.push_back(make_pair(lang.tl("Från klassen"), -1)); for (set::iterator it = fees.begin(); it != fees.end(); ++it) ff.push_back(make_pair(formatCurrency(*it), *it)); gdi.addItem(name, ff); } void oEvent::fillLegNumbers(const set &cls, bool isTeamList, bool includeSubLegs, vector< pair > &out) { oClassList::iterator it; synchronizeList(oLClassId); out.clear(); set< pair > legs; for (it=Classes.begin(); it != Classes.end(); ++it) { if (!it->Removed && (cls.empty() || cls.count(it->getId()))) { if (it->getNumStages() == 0) continue; for (size_t j = 0; j < it->getNumStages(); j++) { int number, order; if (it->splitLegNumberParallel(j, number, order)) { if (order == 0) legs.insert( make_pair(number, 0) ); else { if (it->isOptional(j)) continue; if (order == 1) legs.insert( make_pair(number, 1000)); legs.insert(make_pair(number, 1000+order)); } } else { legs.insert( make_pair(number, 0) ); } } } } out.reserve(legs.size() + 1); for (set< pair >::const_iterator it = legs.begin(); it != legs.end(); ++it) { if (it->second == 0) { out.push_back( make_pair(lang.tl("Sträcka X#" + itos(it->first + 1)), it->first)); } } if (includeSubLegs) { for (set< pair >::const_iterator it = legs.begin(); it != legs.end(); ++it) { if (it->second >= 1000) { int leg = it->first; int sub = it->second - 1000; char bf[64]; char symb = 'a' + sub; sprintf_s(bf, "Sträcka X#%d%c", leg+1, symb); out.push_back( make_pair(lang.tl(bf), (leg + 1) * 10000 + sub)); } } } if (isTeamList) out.push_back(make_pair(lang.tl("Sista sträckan"), 1000)); else out.push_back(make_pair(lang.tl("Alla sträckor"), 1000)); } void oEvent::generateTableData(const string &tname, Table &table, TableUpdateInfo &tui) { if (tname == "runners") { if (tui.doRefresh && !tui.doAdd) return; pRunner r = tui.doAdd ? addRunner(getAutoRunnerName(),0,0,0,0,false) : pRunner(tui.object); generateRunnerTableData(table, r); return; } else if (tname == "classes") { if (tui.doRefresh && !tui.doAdd) return; pClass c = tui.doAdd ? addClass(getAutoClassName()) : pClass(tui.object); generateClassTableData(table, c); return; } else if (tname == "clubs") { if (tui.doRefresh && !tui.doAdd) return; pClub c = tui.doAdd ? addClub("Club", 0) : pClub(tui.object); generateClubTableData(table, c); return; } else if (tname == "teams") { if (tui.doRefresh && !tui.doAdd) return; pTeam t = tui.doAdd ? addTeam(getAutoTeamName()) : pTeam(tui.object); generateTeamTableData(table, t); return; } else if (tname == "cards") { if (tui.doRefresh && !tui.doAdd) return; generateCardTableData(table, pCard(tui.object)); return; } else if (tname == "controls") { if (tui.doRefresh && !tui.doAdd) return; generateControlTableData(table, pControl(tui.object)); return; } else if (tname == "punches") { if (tui.doRefresh && !tui.doAdd) return; pFreePunch c = tui.doAdd ? addFreePunch(0,0,0, false) : pFreePunch(tui.object); generatePunchTableData(table, c); return; } else if (tname == "runnerdb") { if (tui.doAdd || !tui.doRefresh) { oDBRunnerEntry *entry = tui.doAdd ? getRunnerDatabase().addRunner() : (oDBRunnerEntry *)(tui.object); getRunnerDatabase().generateRunnerTableData(table, entry); } if (tui.doRefresh) getRunnerDatabase().refreshRunnerTableData(table); return; } else if (tname == "clubdb") { if (tui.doAdd || !tui.doRefresh) { pClub c = tui.doAdd ? getRunnerDatabase().addClub() : pClub(tui.object); getRunnerDatabase().generateClubTableData(table, c); } if (tui.doRefresh) { getRunnerDatabase().refreshClubTableData(table); } return; } throw std::exception("Wrong table name"); } void oEvent::applyEventFees(bool updateClassFromEvent, bool updateFees, bool updateCardFees, const set &classFilter) { synchronizeList(oLClassId, true, false); synchronizeList(oLRunnerId, false, true); bool allClass = classFilter.empty(); if (updateClassFromEvent) { for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it) { if (it->isRemoved()) continue; if (allClass || classFilter.count(it->getId())) { it->addClassDefaultFee(true); it->synchronize(true); } } } if (updateFees) { for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->skip()) continue; if (allClass || classFilter.count(it->getClassId())) { it->addClassDefaultFee(true); it->synchronize(true); } } } if (updateCardFees) { int cf = getDCI().getInt("CardFee"); for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->skip()) continue; if (it->getDI().getInt("CardFee") != 0) { it->getDI().setInt("CardFee", cf); it->synchronize(true); } } } } #ifndef MEOSDB void hideTabs(); void createTabs(bool force, bool onlyMain, bool skipTeam, bool skipSpeaker, bool skipEconomy, bool skipLists, bool skipRunners, bool skipControls); void oEvent::updateTabs(bool force, bool hide) const { bool hasTeam = !Teams.empty(); for (oClassList::const_iterator it = Classes.begin(); !hasTeam && it!=Classes.end(); ++it) { if (it->getNumStages()>1) hasTeam = true; } bool hasRunner = !Runners.empty() || !Classes.empty(); bool hasLists = !empty(); if (hide || isReadOnly()) hideTabs(); else createTabs(force, empty(), !hasTeam, !getMeOSFeatures().hasFeature(MeOSFeatures::Speaker), !(getMeOSFeatures().hasFeature(MeOSFeatures::Economy) || getMeOSFeatures().hasFeature(MeOSFeatures::EditClub)), !hasLists, !hasRunner, Controls.empty()); } #else void oEvent::updateTabs(bool force) const { } #endif bool oEvent::useRunnerDb() const { return getMeOSFeatures().hasFeature(MeOSFeatures::RunnerDb); //return getDCI().getInt("SkipRunnerDb")==0; } /* void oEvent::useRunnerDb(bool use) { getDI().setInt("SkipRunnerDb", use ? 0 : 1); }*/ bool oEvent::hasMultiRunner() const { for (oClassList::const_iterator it = Classes.begin(); it!=Classes.end(); ++it) { if (it->hasMultiCourse() && it->getNumDistinctRunners() != it->getNumStages()) return true; } return false; } /** Return false if card is not used */ bool oEvent::checkCardUsed(gdioutput &gdi, oRunner &runnerToAssignCard, int cardNo) { synchronizeList(oLRunnerId); //pRunner pold=oe->getRunnerByCardNo(cardNo, 0, true, true); char bf[1024]; pRunner pold = 0; for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->skip()) continue; if (!runnerToAssignCard.canShareCard(&*it, cardNo)) { pold = &*it; break; } } if (pold) { sprintf_s(bf, ("#" + lang.tl("Bricka %d används redan av %s och kan inte tilldelas.")).c_str(), cardNo, pold->getCompleteIdentification().c_str()); gdi.alert(bf); return true; } return false; } void oEvent::removeVacanies(int classId) { oRunnerList::iterator it; vector toRemove; for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->skip() || !it->isVacant()) continue; if (classId!=0 && it->getClassId()!=classId) continue; if (!isRunnerUsed(it->Id)) toRemove.push_back(it->Id); } removeRunner(toRemove); } void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { bool hasResult = false; bool warnNoName = false; bool warnNoClass = false; bool warnNoTeam = false; bool warnNoPatrol = false; bool warnIndividualTeam = false; for (oRunnerList::iterator it = Runners.begin(); it!=Runners.end(); ++it) { if (it->isRemoved()) continue; if (onlyThisClass > 0 && it->getClassId() != onlyThisClass) continue; if (it->sName.empty()) { if (!warnNoName) { warnNoName = true; gdi.alert("Varning: deltagare med blankt namn påträffad. MeOS " "kräver att alla deltagare har ett namn, och tilldelar namnet 'N.N.'"); } it->setName("N.N.", false); it->synchronize(); } if (!it->Class) { if (!warnNoClass) { gdi.alert("Deltagaren 'X' saknar klass.#" + it->getName()); warnNoClass = true; } continue; } if (!it->tInTeam) { ClassType type = it->Class->getClassType(); if (type == oClassIndividRelay) { it->setClassId(it->Class->getId(), true); it->synchronize(); } else if (type == oClassRelay) { if (!warnNoTeam) { gdi.alert("Deltagaren 'X' deltar i stafettklassen 'Y' men saknar lag. Klassens start- " "och resultatlistor kan därmed bli felaktiga.#" + it->getName() + "#" + it->getClass()); warnNoTeam = true; } } else if (type == oClassPatrol) { if (!warnNoPatrol) { gdi.alert("Deltagaren 'X' deltar i patrullklassen 'Y' men saknar patrull. Klassens start- " "och resultatlistor kan därmed bli felaktiga.#" + it->getName() + + "#" + it->getClass()); warnNoPatrol = true; } } } if (it->getFinishTime()>0) hasResult = true; } for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; if (onlyThisClass > 0 && it->getClassId() != onlyThisClass) continue; if (it->sName.empty()) { if (!warnNoName) { warnNoName = true; gdi.alert("Varning: lag utan namn påträffat. " "MeOS kräver att alla lag har ett namn, och tilldelar namnet 'N.N.'"); } it->setName("N.N.", false); it->synchronize(); } if (!it->Class) { if (!warnNoClass) { gdi.alert("Laget 'X' saknar klass.#" + it->getName()); warnNoClass = true; } continue; } ClassType type = it->Class->getClassType(); if (type == oClassIndividual) { if (!warnIndividualTeam) { gdi.alert("Laget 'X' deltar i individuella klassen 'Y'. Klassens start- och resultatlistor " "kan därmed bli felaktiga.#" + it->getName() + "#" + it->getClass()); warnIndividualTeam = true; } } } if (expectResult && !hasResult) gdi.alert("Tävlingen innehåller inga resultat."); bool warnBadStart = false; for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it) { if (it->isRemoved()) continue; if (it->getClassStatus() != oClass::Normal) continue; if (onlyThisClass > 0 && it->getId() != onlyThisClass) continue; if (it->hasMultiCourse()) { for (unsigned k=0;kgetNumStages(); k++) { StartTypes st = it->getStartType(k); LegTypes lt = it->getLegType(k); if (k==0 && (st == STChange || st == STHunting) && !warnBadStart) { warnBadStart = true; gdi.alert("Klassen 'X' har jaktstart/växling på första sträckan.#" + it->getName()); } if (st == STTime && it->getStartData(k)<=0 && !warnBadStart && (lt == LTNormal || lt == LTSum)) { warnBadStart = true; gdi.alert("Ogiltig starttid i 'X' på sträcka Y.#" + it->getName() + "#" + itos(k+1)); } } } } } HWND oEvent::hWnd() const { return gdibase.getHWND(); } oTimeLine::~oTimeLine() { } void oEvent::remove() { if (isClient()) dropDatabase(); else deleteCompetition(); clearListedCmp(); newCompetition(""); } bool oEvent::canRemove() const { return true; } string oEvent::formatCurrency(int c, bool includeSymbol) const { if (tCurrencyFactor == 1) if (!includeSymbol) return itos(c); else if (tCurrencyPreSymbol) return tCurrencySymbol + itos(c); else return itos(c) + tCurrencySymbol; else { char bf[32]; if (includeSymbol) { sprintf_s(bf, 32, "%d%s%02d", c/tCurrencyFactor, tCurrencySeparator.c_str(), c%tCurrencyFactor); if (tCurrencyPreSymbol) return tCurrencySymbol + bf; else return bf + tCurrencySymbol; } else { sprintf_s(bf, 32, "%d.%02d", c/tCurrencyFactor, c%tCurrencyFactor); return bf; } } } int oEvent::interpretCurrency(const string &c) const { if (tCurrencyFactor == 1 && tCurrencyPreSymbol == false) return atoi(c.c_str()); size_t s = 0; while (s < c.length() && (c[s]<'0' || c[s]>'9')) s++; string cc = c.substr(s); for (size_t k = 0; k 0) { if (tCurrencyPreSymbol) { char end = *tCurrencySymbol.rbegin(); if ((end>='a' && end <='z') || end>='A' && end <='Z') tCurrencySymbol += " "; } else { char end = *tCurrencySymbol.begin(); if ((end>='a' && end <='z') || end>='A' && end <='Z') tCurrencySymbol = " " + tCurrencySymbol; } } } else { tCurrencyFactor = factor; tCurrencySymbol = symbol; tCurrencySeparator = separator; tCurrencyPreSymbol = preSymbol; getDI().setString("CurrencySymbol", symbol); getDI().setInt("CurrencyFactor", factor); getDI().setString("CurrencySeparator", separator); getDI().setInt("CurrencyPreSymbol", preSymbol ? 1 : 0); } } string oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, bool cloneCourses, bool cloneResult, bool addToDate) { if (cloneResult) { cloneTimes = true; cloneCourses = true; } if (cloneTimes) cloneRunners = true; oEvent ce(gdibase); ce.newCompetition(Name); ce.ZeroTime = ZeroTime; ce.Date = Date; if (addToDate) { SYSTEMTIME st; convertDateYMS(Date, st, false); __int64 absD = SystemTimeToInt64Second(st); absD += 3600*24; ce.Date = convertSystemDate(Int64SecondToSystemTime(absD)); } int len = Name.length(); if (len > 2 && isdigit(Name[len-1]) && !isdigit(Name[len-2])) { ++ce.Name[len-1]; // E1 -> E2 } else ce.Name += " E2"; memcpy(ce.oData, oData, sizeof(oData)); for (oClubList::iterator it = Clubs.begin(); it != Clubs.end(); ++it) { if (it->isRemoved()) continue; pClub pc = ce.addClub(it->name, it->Id); memcpy(pc->oData, it->oData, sizeof(pc->oData)); } if (cloneCourses) { for (oControlList::iterator it = Controls.begin(); it != Controls.end(); ++it) { if (it->isRemoved()) continue; pControl pc = ce.addControl(it->Id, 100, it->Name); pc->setNumbers(it->codeNumbers()); pc->Status = it->Status; memcpy(pc->oData, it->oData, sizeof(pc->oData)); } for (oCourseList::iterator it = Courses.begin(); it != Courses.end(); ++it) { if (it->isRemoved()) continue; pCourse pc = ce.addCourse(it->Name, it->Length, it->Id); pc->importControls(it->getControls(), false); pc->legLengths = it->legLengths; memcpy(pc->oData, it->oData, sizeof(pc->oData)); } } for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it) { if (it->isRemoved()) continue; pClass pc = ce.addClass(it->Name, 0, it->Id); memcpy(pc->oData, it->oData, sizeof(pc->oData)); pc->setNumStages(it->getNumStages()); pc->legInfo = it->legInfo; if (cloneCourses) { pc->Course = ce.getCourse(it->getCourseId()); pc->MultiCourse = it->MultiCourse; // Points to wrong competition, but valid for now... } } if (cloneRunners) { for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; oRunner r(&ce, it->Id); r.sName = it->sName; oRunner::getRealName(r.sName, r.tRealName); r.StartNo = it->StartNo; r.CardNo = it->CardNo; r.Club = ce.getClub(it->getClubId()); r.Class = ce.getClass(it->getClassId()); if (cloneCourses) r.Course = ce.getCourse(it->getCourseId()); pRunner pr = ce.addRunner(r, false); pr->decodeMultiR(it->codeMultiR()); memcpy(pr->oData, it->oData, sizeof(pr->oData)); if (cloneTimes) { pr->startTime = it->startTime; } if (cloneResult) { if (it->Card) { pr->Card = ce.addCard(*it->Card); pr->Card->tOwner = pr; } pr->FinishTime = it->FinishTime; pr->status = it->status; } } for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) { if (it->skip()) continue; oTeam t(&ce, it->Id); t.sName = it->sName; t.StartNo = it->StartNo; t.Club = ce.getClub(it->getClubId()); t.Class = ce.getClass(it->getClassId()); if (cloneTimes) t.startTime = it->startTime; pTeam pt = ce.addTeam(t, false); memcpy(pt->oData, it->oData, sizeof(pt->oData)); pt->Runners.resize(it->Runners.size()); for (size_t k = 0; kRunners.size(); k++) { int id = it->Runners[k] ? it->Runners[k]->Id : 0; if (id) pt->Runners[k] = ce.getRunner(id, 0); } t.apply(false, 0, false); } for (oRunnerList::iterator it = ce.Runners.begin(); it != ce.Runners.end(); ++it) { it->createMultiRunner(false, false); } } vector changedClass, changedClassNoResult, assignedVacant, newEntries, notTransfered, noAssign; set dummy; transferResult(ce, dummy, TransferAnyway, false, changedClass, changedClassNoResult, assignedVacant, newEntries, notTransfered, noAssign); vector newEntriesT, notTransferedT, noAssignT; transferResult(ce, TransferAnyway, newEntriesT, notTransferedT, noAssignT); int eventNumberCurrent = getStageNumber(); if (eventNumberCurrent <= 0) { eventNumberCurrent = 1; setStageNumber(eventNumberCurrent); } ce.getDI().setString("PreEvent", CurrentNameId); ce.setStageNumber(eventNumberCurrent + 1); getDI().setString("PostEvent", ce.CurrentNameId); int nf = getMeOSFeatures().getNumFeatures(); for (int k = 0; k < nf; k++) { MeOSFeatures::Feature f = getMeOSFeatures().getFeature(k); if (getMeOSFeatures().hasFeature(f)) ce.getMeOSFeatures().useFeature(f, true, ce); } ce.save(); return ce.CurrentFile; } bool checkTargetClass(pRunner target, pRunner source, const oClassList &Classes, const vector &targetVacant, vector &changedClass, oEvent::ChangedClassMethod changeClassMethod) { if (changeClassMethod == oEvent::TransferAnyway) return true; if (!compareClassName(target->getClass(), source->getClass())) { // Store all vacant positions in the right class int targetClass = -1; if (target->getStatus() == StatusOK) { // There is already a result. Do not change class! return false; } if (changeClassMethod == oEvent::TransferNoResult) return false; // Do not allow change class, do not transfer result for (oClassList::const_iterator cit = Classes.begin(); cit != Classes.end(); ++cit) { if (cit->isRemoved()) continue; if (compareClassName(cit->getName(), source->getClass())) { targetClass = cit->getId(); if (targetClass == source->getClassId() || cit->getName() == source->getClass()) break; // Assume exact match } } if (targetClass != -1) { set vacantIx; for (size_t j = 0; j < targetVacant.size(); j++) { if (!targetVacant[j]) continue; if (targetVacant[j]->getClassId() == targetClass) vacantIx.insert(j); } int posToUse = -1; if (vacantIx.size() == 1) posToUse = *vacantIx.begin(); else if (vacantIx.size() > 1) { string srcBib = source->getBib(); if (srcBib.length() > 0) { for (set::iterator tit = vacantIx.begin(); tit != vacantIx.end(); ++tit) { if (targetVacant[*tit]->getBib() == srcBib) { posToUse = *tit; break; } } } if (posToUse == -1) posToUse = *vacantIx.begin(); } if (posToUse != -1) { // Change class or change class vacant changedClass.push_back(target); int oldStart = target->getStartTime(); string oldBib = target->getBib(); int oldSN = target->getStartNo(); int oldClass = target->getClassId(); pRunner tgt = targetVacant[posToUse]; target->cloneStartTime(tgt); target->setBib(tgt->getBib(), 0, false, false); target->setStartNo(tgt->getStartNo(), false); target->setClassId(tgt->getClassId(), false); tgt->setStartTime(oldStart, true, false); tgt->setBib(oldBib, 0, false, false); tgt->setStartNo(oldSN, false); tgt->setClassId(oldClass, false); return true; // Changed to correct class } else if (changeClassMethod == oEvent::ChangeClass) { // Simpliy change class target->setClassId(targetClass, false); return true; } } return false; // Wrong class, ChangeClass (but failed) } return true; // Same class, OK } void oEvent::transferResult(oEvent &ce, const set &allowNewEntries, ChangedClassMethod changeClassMethod, bool transferAllNoCompete, vector &changedClass, vector &changedClassNoResult, vector &assignedVacant, vector &newEntries, vector ¬Transfered, vector &noAssignmentTarget) { inthashmap processed(ce.Runners.size()); inthashmap used(Runners.size()); changedClass.clear(); changedClassNoResult.clear(); assignedVacant.clear(); newEntries.clear(); notTransfered.clear(); noAssignmentTarget.clear(); vector targetRunners; vector targetVacant; targetRunners.reserve(ce.Runners.size()); for (oRunnerList::iterator it = ce.Runners.begin(); it != ce.Runners.end(); ++it) { if (!it->skip()) { if (!it->isVacant()) targetRunners.push_back(&*it); else targetVacant.push_back(&*it); } } calculateResults(RTTotalResult); // Lookup by id for (size_t k = 0; k < targetRunners.size(); k++) { pRunner it = targetRunners[k]; pRunner r = getRunner(it->Id, 0); if (!r) continue; __int64 id1 = r->getExtIdentifier(); __int64 id2 = it->getExtIdentifier(); if (id1>0 && id2>0 && id1 != id2) continue; string cnA = canonizeName(it->sName.c_str()); string cnB = canonizeName(r->sName.c_str()); string ccnA = canonizeName(it->getClub().c_str()); string ccnB = canonizeName(r->getClub().c_str()); if ((id1>0 && id1==id2) || (r->CardNo>0 && r->CardNo == it->CardNo) || (it->sName == r->sName) || (cnA == cnB && ccnA == ccnB)) { processed.insert(it->Id, 1); used.insert(r->Id, 1); if (checkTargetClass(it, r, ce.Classes, targetVacant, changedClass, changeClassMethod)) it->setInputData(*r); else { it->resetInputData(); changedClassNoResult.push_back(it); } } } if (processed.size() < int(targetRunners.size())) { // Lookup by card int v; setupCardHash(false); for (size_t k = 0; k < targetRunners.size(); k++) { pRunner it = targetRunners[k]; if (processed.lookup(it->Id, v)) continue; if (it->CardNo > 0) { pRunner r = getRunnerByCardNo(it->CardNo, 0); if (!r || used.lookup(r->Id, v)) continue; __int64 id1 = r->getExtIdentifier(); __int64 id2 = it->getExtIdentifier(); if (id1>0 && id2>0 && id1 != id2) continue; if ((id1>0 && id1==id2) || (it->sName == r->sName && it->getClub() == r->getClub())) { processed.insert(it->Id, 1); used.insert(r->Id, 1); if (checkTargetClass(it, r, ce.Classes, targetVacant, changedClass, changeClassMethod)) it->setInputData(*r); else { it->resetInputData(); changedClassNoResult.push_back(it); } } } } setupCardHash(true); // Clear cache } int v = -1; // Store remaining runners vector remainingRunners; for (oRunnerList::iterator it2 = Runners.begin(); it2 != Runners.end(); ++it2) { if (it2->skip() || used.lookup(it2->Id, v)) continue; if (it2->isVacant()) continue; // Ignore vacancies on source side remainingRunners.push_back(&*it2); } if (processed.size() < int(targetRunners.size()) && !remainingRunners.empty()) { // Lookup by name / ext id vector cnd; for (size_t k = 0; k < targetRunners.size(); k++) { pRunner it = targetRunners[k]; if (processed.lookup(it->Id, v)) continue; __int64 id1 = it->getExtIdentifier(); cnd.clear(); for (size_t j = 0; j < remainingRunners.size(); j++) { pRunner src = remainingRunners[j]; if (!src) continue; if (id1 > 0) { __int64 id2 = src->getExtIdentifier(); if (id2 == id1) { cnd.clear(); cnd.push_back(j); break; //This is the one, if they have the same Id there will be a unique match below } } if (it->sName == src->sName && it->getClub() == src->getClub()) cnd.push_back(j); } if (cnd.size() == 1) { pRunner &src = remainingRunners[cnd[0]]; processed.insert(it->Id, 1); used.insert(src->Id, 1); if (checkTargetClass(it, src, ce.Classes, targetVacant, changedClass, changeClassMethod)) { it->setInputData(*src); } else { it->resetInputData(); changedClassNoResult.push_back(it); } src = 0; } else if (cnd.size() > 0) { // More than one candidate int winnerIx = -1; int point = -1; for (size_t j = 0; j < cnd.size(); j++) { pRunner src = remainingRunners[cnd[j]]; int p = 0; if (src->getClass() == it->getClass()) p += 1; if (src->getBirthYear() == it->getBirthYear()) p += 2; if (p > point) { winnerIx = cnd[j]; point = p; } } if (winnerIx != -1) { processed.insert(it->Id, 1); pRunner winner = remainingRunners[winnerIx]; remainingRunners[winnerIx] = 0; used.insert(winner->Id, 1); if (checkTargetClass(it, winner, ce.Classes, targetVacant, changedClass, changeClassMethod)) { it->setInputData(*winner); } else { it->resetInputData(); changedClassNoResult.push_back(it); } } } } } // Transfer vacancies for (size_t k = 0; k < remainingRunners.size(); k++) { pRunner src = remainingRunners[k]; if (!src || used.lookup(src->Id, v)) continue; bool forceSkip = src->hasFlag(oAbstractRunner::FlagTransferSpecified) && !src->hasFlag(oAbstractRunner::FlagTransferNew); if (forceSkip) { notTransfered.push_back(src); continue; } pRunner targetVacant = ce.getRunner(src->getId(), 0); if (targetVacant && targetVacant->isVacant() && compareClassName(targetVacant->getClass(), src->getClass()) ) { targetVacant->setName(src->getName(), false); targetVacant->setClub(src->getClub()); targetVacant->setCardNo(src->getCardNo(), false); targetVacant->cloneData(src); assignedVacant.push_back(targetVacant); } else { pClass dstClass = ce.getClass(src->getClassId()); if (dstClass && compareClassName(dstClass->getName(), src->getClass())) { if ( (!src->hasFlag(oAbstractRunner::FlagTransferSpecified) && allowNewEntries.count(src->getClassId())) || src->hasFlag(oAbstractRunner::FlagTransferNew)) { if (src->getClubId() > 0) ce.getClubCreate(src->getClubId(), src->getClub()); pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(), src->getCardNo(), src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); newEntries.push_back(dst); } else if (transferAllNoCompete) { if (src->getClubId() > 0) ce.getClubCreate(src->getClubId(), src->getClub()); pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(), 0, src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); dst->setStatus(StatusNotCompetiting, true, false); notTransfered.push_back(dst); } else notTransfered.push_back(src); } } } // Runners on target side not assigned a result for (size_t k = 0; k < targetRunners.size(); k++) { if (targetRunners[k] && !processed.count(targetRunners[k]->Id)) { noAssignmentTarget.push_back(targetRunners[k]); if (targetRunners[k]->inputStatus == StatusUnknown || (targetRunners[k]->inputStatus == StatusOK && targetRunners[k]->inputTime == 0)) { targetRunners[k]->inputStatus = StatusNotCompetiting; } } } } void oEvent::transferResult(oEvent &ce, ChangedClassMethod changeClassMethod, vector &newEntries, vector ¬Transfered, vector &noAssignmentTarget) { inthashmap processed(ce.Teams.size()); inthashmap used(Teams.size()); newEntries.clear(); notTransfered.clear(); noAssignmentTarget.clear(); vector targetTeams; targetTeams.reserve(ce.Teams.size()); for (oTeamList::iterator it = ce.Teams.begin(); it != ce.Teams.end(); ++it) { if (!it->skip()) { targetTeams.push_back(&*it); } } calculateTeamResults(true); // Lookup by id for (size_t k = 0; k < targetTeams.size(); k++) { pTeam it = targetTeams[k]; pTeam t = getTeam(it->Id); if (!t) continue; __int64 id1 = t->getExtIdentifier(); __int64 id2 = it->getExtIdentifier(); if (id1>0 && id2>0 && id1 != id2) continue; if ((id1>0 && id1==id2) || (it->sName == t->sName && it->getClub() == t->getClub())) { processed.insert(it->Id, 1); used.insert(t->Id, 1); it->setInputData(*t); //checkTargetClass(it, r, ce.Classes, targetVacant, changedClass); } } int v = -1; // Store remaining runners vector remainingTeams; for (oTeamList::iterator it2 = Teams.begin(); it2 != Teams.end(); ++it2) { if (it2->skip() || used.lookup(it2->Id, v)) continue; if (it2->isVacant()) continue; // Ignore vacancies on source side remainingTeams.push_back(&*it2); } if (processed.size() < int(targetTeams.size()) && !remainingTeams.empty()) { // Lookup by name / ext id vector cnd; for (size_t k = 0; k < targetTeams.size(); k++) { pTeam it = targetTeams[k]; if (processed.lookup(it->Id, v)) continue; __int64 id1 = it->getExtIdentifier(); cnd.clear(); for (size_t j = 0; j < remainingTeams.size(); j++) { pTeam src = remainingTeams[j]; if (!src) continue; if (id1 > 0) { __int64 id2 = src->getExtIdentifier(); if (id2 == id1) { cnd.clear(); cnd.push_back(j); break; //This is the one, if they have the same Id there will be a unique match below } } if (it->sName == src->sName && it->getClub() == src->getClub()) cnd.push_back(j); } if (cnd.size() == 1) { pTeam &src = remainingTeams[cnd[0]]; processed.insert(it->Id, 1); used.insert(src->Id, 1); it->setInputData(*src); //checkTargetClass(it, src, ce.Classes, targetVacant, changedClass); src = 0; } else if (cnd.size() > 0) { // More than one candidate int winnerIx = -1; int point = -1; for (size_t j = 0; j < cnd.size(); j++) { pTeam src = remainingTeams[cnd[j]]; int p = 0; if (src->getClass() == it->getClass()) p += 1; if (p > point) { winnerIx = cnd[j]; point = p; } } if (winnerIx != -1) { processed.insert(it->Id, 1); pTeam winner = remainingTeams[winnerIx]; remainingTeams[winnerIx] = 0; used.insert(winner->Id, 1); it->setInputData(*winner); //checkTargetClass(it, winner, ce.Classes, targetVacant, changedClass); } } } } /* // Transfer vacancies for (size_t k = 0; k < remainingRunners.size(); k++) { pRunner src = remainingRunners[k]; if (!src || used.lookup(src->Id, v)) continue; pRunner targetVacant = ce.getRunner(src->getId(), 0); if (targetVacant && targetVacant->isVacant() && compareClassName(targetVacant->getClass(), src->getClass()) ) { targetVacant->setName(src->getName()); targetVacant->setClub(src->getClub()); targetVacant->setCardNo(src->getCardNo(), false); targetVacant->cloneData(src); assignedVacant.push_back(targetVacant); } else { pClass dstClass = ce.getClass(src->getClassId()); if (dstClass && compareClassName(dstClass->getName(), src->getClass())) { if (allowNewEntries.count(src->getClassId())) { if (src->getClubId() > 0) ce.getClubCreate(src->getClubId(), src->getClub()); pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(), src->getCardNo(), src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); newEntries.push_back(dst); } else if (transferAllNoCompete) { if (src->getClubId() > 0) ce.getClubCreate(src->getClubId(), src->getClub()); pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(), 0, src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); dst->setStatus(StatusNotCompetiting); notTransfered.push_back(dst); } else notTransfered.push_back(src); } } } // Runners on target side not assigned a result for (size_t k = 0; k < targetRunners.size(); k++) { if (targetRunners[k] && !processed.count(targetRunners[k]->Id)) { noAssignmentTarget.push_back(targetRunners[k]); if (targetRunners[k]->inputStatus == StatusUnknown || (targetRunners[k]->inputStatus == StatusOK && targetRunners[k]->inputTime == 0)) { targetRunners[k]->inputStatus = StatusNotCompetiting; } } }*/ } MetaListContainer &oEvent::getListContainer() const { if (!listContainer) throw std::exception("Nullpointer exception"); return *listContainer; } void oEvent::setExtraLines(const char *attrib, const vector< pair > &lines) { string str; for(size_t k = 0; k < lines.size(); k++) { if (k>0) str.push_back('|'); string msg = lines[k].first; for (size_t i = 0; i < msg.size(); i++) { if (msg[i] == '|') str.push_back(':'); // Encoding does not support | else str.push_back(msg[i]); } str.push_back('|'); str.append(itos(lines[k].second)); } getDI().setString(attrib, str); } void oEvent::getExtraLines(const char *attrib, vector< pair > &lines) const { vector splt; const string &splitPrintExtra = getDCI().getString(attrib); split(splitPrintExtra, "|", splt); lines.clear(); lines.reserve(splt.size() / 2); for (size_t k = 0; k + 1 < splt.size(); k+=2) { lines.push_back(make_pair(splt[k], atoi(splt[k+1].c_str()))); } while(!lines.empty()) { if (lines.back().first.length() == 0) lines.pop_back(); else break; } } oEvent::MultiStageType oEvent::getMultiStageType() const { if (getDCI().getString("PreEvent").empty()) return MultiStageNone; else return MultiStageSameEntry; } bool oEvent::hasNextStage() const { return !getDCI().getString("PostEvent").empty(); } bool oEvent::hasPrevStage() const { return !getDCI().getString("PreEvent").empty(); } int oEvent::getNumStages() const { int ns = getDCI().getInt("NumStages"); if (ns>0) return ns; else return 1; } void oEvent::setNumStages(int numStages) { getDI().setInt("NumStages", numStages); } int oEvent::getStageNumber() const { return getDCI().getInt("EventNumber"); } void oEvent::setStageNumber(int num) { getDI().setInt("EventNumber", num); } oDataContainer &oEvent::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { data = (pvoid)oData; olddata = (pvoid)oDataOld; strData = (pvectorstr)&dynamicData; return *oEventData; } void oEvent::changedObject() { globalModification = true; } void oEvent::pushDirectChange() { PostMessage(gdibase.getMain(), WM_USER + 4, 0, 0); } int oEvent::getBibClassGap() const { int ns = getDCI().getInt("BibGap"); return ns; } void oEvent::setBibClassGap(int numStages) { getDI().setInt("BibGap", numStages); } void oEvent::checkNecessaryFeatures() { bool hasMultiRace = false; bool hasRelay = false; bool hasPatrol = false; bool hasForkedIndividual = false; for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it) { const oClass &c = *it; bool multiRace = false; bool relay = false; bool patrol = false; for (size_t j = 0; j < c.legInfo.size(); j++) { if (c.legInfo[j].duplicateRunner != -1) multiRace = true; if (j > 0 && !c.legInfo[j].isParallel() && !c.legInfo[j].isOptional()) { relay = true; patrol = false; } if (j > 0 && (c.legInfo[j].isParallel() || c.legInfo[j].isOptional()) && !relay) { patrol = true; } } hasForkedIndividual |= c.legInfo.size() == 1; hasMultiRace |= multiRace; hasRelay |= relay; hasPatrol |= patrol; } if (hasForkedIndividual) oe->getMeOSFeatures().useFeature(MeOSFeatures::ForkedIndividual, true, *this); if (hasRelay) oe->getMeOSFeatures().useFeature(MeOSFeatures::Relay, true, *this); if (hasPatrol) oe->getMeOSFeatures().useFeature(MeOSFeatures::Patrol, true, *this); if (hasMultiRace) oe->getMeOSFeatures().useFeature(MeOSFeatures::MultipleRaces, true, *this); oe->synchronize(true); } bool oEvent::useLongTimes() const { if (tLongTimesCached != -1) return tLongTimesCached != 0; tLongTimesCached = getDCI().getInt("LongTimes"); return tLongTimesCached != 0; } void oEvent::useLongTimes(bool use) { tLongTimesCached = use; getDI().setInt("LongTimes", use ? 1 : 0); } int oEvent::convertToFullTime(int inTime) { if (inTime < 0 || !useLongTimes() || inTime > 24*3600) return inTime; return inTime; } void oEvent::getPayModes(vector< pair > &modes) { modes.clear(); modes.reserve(10); vector< pair > lines; getExtraLines("PayModes", lines); modes.push_back(make_pair(lang.tl("Kontant betalning"), 0)); map id2ix; id2ix[0] = 0; for (size_t k = 0; k < lines.size(); k++) { int id = lines[k].second; if (id2ix.count(id)) modes[id2ix[id]].first = lines[k].first; else { id2ix[id] = k; modes.push_back(make_pair(lines[k].first, id)); } } } void oEvent::setPayMode(int id, const string &mode) { vector< pair > lines; getExtraLines("PayModes", lines); if (mode.empty()) { // Remove for (size_t k = 0; k < lines.size(); k++) { if (lines[k].second == id) { bool valid = id != 0; for (oRunnerList::const_iterator it = Runners.begin(); valid && it != Runners.end(); ++it) { if (it->getPaymentMode() == id) valid = false; } for (oTeamList::const_iterator it = Teams.begin(); valid && it != Teams.end(); ++it) { if (it->getPaymentMode() == id) valid = false; } if (!valid) throw meosException("Betalningsättet behövs och kan inte tas bort."); lines.erase(lines.begin() + k); k--; } } } else { // Add / update bool done = false; for (size_t k = 0; k < lines.size(); k++) { if (lines[k].second == id) { lines[k].first = mode; done = true; break; } } if (!done) { lines.push_back(make_pair(mode, id)); } } setExtraLines("PayModes", lines); } void oEvent::useDefaultProperties(bool useDefault) { if (useDefault) { if (savedProperties.empty()) savedProperties.swap(eventProperties); } else { if (!savedProperties.empty()) { savedProperties.swap(eventProperties); savedProperties.clear(); } } }