/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2024 Melin Software HB
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "stdafx.h"
#include "resource.h"
#include
#include
#include "oEvent.h"
#include "gdioutput.h"
#include "gdiconstants.h"
#include "meos_util.h"
#include "TabCompetition.h"
#include "localizer.h"
#include "meosException.h"
#include "MeOSFeatures.h"
#include "importformats.h"
#include
#include
#include
#include
#include "csvparser.h"
int CompetitionCB(gdioutput *gdi, GuiEventType type, BaseInfo* data);
int NewGuideCB(gdioutput *gdi, GuiEventType type, BaseInfo* data) {
TabCompetition &tc = dynamic_cast(*gdi->getTabs().get(TCmpTab));
return tc.newGuideCB(*gdi, type, data);
}
int TabCompetition::newGuideCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
if (type == GUI_LINK) {
TextInfo ti = *(TextInfo *)data;
if (ti.id == "link") {
}
}
else if (type==GUI_BUTTON) {
ButtonInfo bi=*(ButtonInfo *)data;
if (bi.id == "ImportEntries") {
newCompetitionGuide(gdi, 1);
}
else if (bi.id == "BasicSetup") {
gdi.restore("entrychoice");
entryChoice(gdi);
gdi.refresh();
}
else if (bi.id == "DoImportEntries") {
auto baseOnId = gdi.getSelectedItem("CmpSel");
createCompetition(gdi);
try {
gdi.autoRefresh(true);
FlowOperation res = saveEntries(gdi, false, 0, true);
if (res != FlowContinue) {
if (res == FlowCancel)
newCompetitionGuide(gdi, 1);
return 0;
}
}
catch (std::exception &) {
newCompetitionGuide(gdi, 1);
throw;
}
gdi.restore("newcmp");
gdi.setCX(gdi.getCX() + gdi.getLineHeight());
gdi.pushX();
gdi.setRestorePoint("entrychoice");
if (!baseOnId.second || baseOnId.first <= 0)
newCompetitionGuide(gdi, 2);
else
createNewCmp(gdi, true);
}
else if (bi.id == "NoEntries") {
gdi.restore("entrychoice");
auto baseOnId = gdi.getSelectedItem("CmpSel");
if (baseOnId.second && baseOnId.first <= 0)
newCompetitionGuide(gdi, 2);
else
createNewCmp(gdi, false);
}
else if (bi.id == "Cancel") {
oe->clear();
oe->updateTabs(true, false);
loadPage(gdi);
}
else if (bi.id == "FAll") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
oe->getMeOSFeatures().useAll(*oe);
createNewCmp(gdi, true);
}
else if (bi.id == "FBasic") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
oe->getMeOSFeatures().clear(*oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Clubs, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::RunnerDb, true, *oe);
createNewCmp(gdi, true);
}
else if (bi.id == "FSelect") {
newCompetitionGuide(gdi, 3);
}
else if (bi.id == "StoreFeatures") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
saveMeosFeatures(gdi, true);
createNewCmp(gdi, true);
}
else if (bi.id == "FIndividual") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::EditClub, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Network, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Vacancy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::InForest, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::DrawStartList, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Bib, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::RunnerDb, true, *oe);
createNewCmp(gdi, true);
}
else if (bi.id == "FNoCourses" || bi.id == "FNoCoursesRelay") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::EditClub, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Network, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Vacancy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::DrawStartList, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Bib, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::RunnerDb, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::NoCourses, true, *oe);
if (bi.id == "FNoCoursesRelay")
oe->getMeOSFeatures().useFeature(MeOSFeatures::Relay, true, *oe);
createNewCmp(gdi, true);
}
else if (bi.id == "FForked") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::EditClub, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Network, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Vacancy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::InForest, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::DrawStartList, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Bib, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::RunnerDb, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::ForkedIndividual, true, *oe);
createNewCmp(gdi, true);
}
else if (bi.id == "FTeam") {
if (gdi.hasWidget("Name"))
createCompetition(gdi);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::EditClub, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Network, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Vacancy, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::InForest, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::DrawStartList, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Bib, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::RunnerDb, true, *oe);
oe->getMeOSFeatures().useFeature(MeOSFeatures::Relay, true, *oe);
if (oe->hasMultiRunner())
oe->getMeOSFeatures().useFeature(MeOSFeatures::MultipleRaces, true, *oe);
createNewCmp(gdi, true);
}
}
else if (type == GUI_INPUT) {
InputInfo &ii = *(InputInfo*)data;
if (ii.id == "FirstStart" || ii.id == "Date") {
int t,d;
SYSTEMTIME st;
if (ii.id == "FirstStart") {
t = convertAbsoluteTimeHMS(ii.text, -1);
d = convertDateYMD(gdi.getText("Date"), st, true);
ii.setBgColor(t == -1 ? colorLightRed: colorDefault);
}
else {
t = convertAbsoluteTimeHMS(gdi.getText("FirstStart"), -1);
d = convertDateYMD(ii.text, st, true);
ii.setBgColor(d <= 0 ? colorLightRed: colorDefault);
}
if (t <= 0 || d <= 0) {
gdi.setTextTranslate("AllowedInterval", L"Felaktigt datum/tid", true);
}
else {
long long absT = SystemTimeToInt64TenthSecond(st);
absT += max(0, t - timeConstHour);
long long stopT = absT + 23 * timeConstHour;
SYSTEMTIME start = Int64TenthSecondToSystemTime(absT);
SYSTEMTIME end = Int64TenthSecondToSystemTime(stopT);
wstring s = L"Tävlingen måste avgöras mellan X och Y.#" + convertSystemTime(start) + L"#" + convertSystemTime(end);
gdi.setTextTranslate("AllowedInterval", s, true);
}
}
}
return 0;
}
void TabCompetition::newCompetitionGuide(gdioutput &gdi, int step) {
static RECT rc;
const int width = 600;
if (step == 0) {
oe->updateTabs(true, true);
gdi.clearPage(false);
gdi.addString("", boldLarge, "Ny tävling");
gdi.dropLine();
gdi.setRestorePoint("newcmp");
rc.top = gdi.getCY();
rc.left = gdi.getCX();
gdi.dropLine();
gdi.setCX(gdi.getCX() + gdi.getLineHeight());
gdi.addString("", fontMediumPlus, "Namn och tidpunkt");
gdi.dropLine(0.5);
gdi.pushX(); // save start of line
gdi.fillRight();
gdi.addInput("Name", lang.tl("Ny tävling"), 34, 0, L"Tävlingens namn:");
gdi.dropLine(1.5);
auto xx = gdi.getCX(); // Save checkBox location
gdi.addCheckbox("UseFileEventName", L"NewCmp:FromFile", NewGuideCB, true, L"");
gdi.popX(); gdi.pushX(); // reset and re-save start of line
gdi.fillRight();
gdi.dropLine(2.0);
InputInfo &date = gdi.addInput("Date", getLocalDate(), 16, nullptr, L"Datum (för första start):");
gdi.addInput("FirstStart", L"07:00:00", 12, NewGuideCB, L"Första tillåtna starttid:");
gdi.dropLine(1.5);
gdi.setCX(xx); // line up under previous checkBox
gdi.addCheckbox("UseFileEventDate", L"NewCmp:FromFile", nullptr, true, L"");
gdi.popX();
gdi.fillDown();
gdi.dropLine(3.5);
gdi.addString("AllowedInterval", 0, "");
newGuideCB(gdi, GUI_INPUT, &date);
gdi.dropLine(2);
gdi.addSelection("CmpSel", 300, 400, CompetitionCB, L"Basera på en tidigare tävling:");
gdi.addItem("CmpSel", lang.tl("Ingen[competition]"), -1);
oe->fillCompetitions(gdi, "CmpSel", 0, L"", false);
gdi.autoGrow("CmpSel");
gdi.selectFirstItem("CmpSel");
rc.right = rc.left + gdi.scaleLength(width);
rc.bottom = gdi.getCY();
gdi.addRectangle(rc, colorLightBlue, true);
gdi.dropLine();
gdi.setRestorePoint("entrychoice");
entryChoice(gdi);
gdi.refresh();
}
else if (step == 1) {
gdi.restore("entrychoice");
rc.top = gdi.getCY();
gdi.fillDown();
entryForm(gdi, true);
rc.bottom = gdi.getCY();
gdi.dropLine();
gdi.addRectangle(rc, colorLightBlue, true);
gdi.fillRight();
gdi.addButton("BasicSetup", "<< Bakåt", NewGuideCB);
gdi.addButton("DoImportEntries", "Importera", NewGuideCB);
gdi.addButton("Cancel", "Avbryt", NewGuideCB).setCancel();
gdi.popX();
gdi.dropLine(2);
gdi.fillDown();
gdi.scrollToBottom();
gdi.refresh();
}
else if (step == 2) {
rc.top = gdi.getCY();
oe->updateTabs(true, true);
gdi.fillDown();
gdi.dropLine();
gdi.addString("", fontMediumPlus, "Funktioner i MeOS");
gdi.dropLine(0.5);
gdi.addString("", 10, "newcmp:featuredesc");
gdi.dropLine();
gdi.addString("", 1, "Välj vilka funktioner du vill använda");
gdi.dropLine(0.5);
gdi.fillRight();
gdi.addString("", 1, "Individuellt").setColor(colorDarkBlue);
gdi.popX();
gdi.dropLine(1.2);
gdi.addButton("FIndividual", "Individuell tävling", NewGuideCB);
gdi.addButton("FForked", "Individuellt, gafflat", NewGuideCB);
gdi.popX();
gdi.dropLine(2);
gdi.addButton("FBasic", "Endast grundläggande (enklast möjligt)", NewGuideCB);
gdi.addButton("FNoCourses", "Endast tidtagning (utan banor)", NewGuideCB);
gdi.popX();
gdi.dropLine(3);
gdi.addString("", 1, "Lag och stafett").setColor(colorDarkBlue);
gdi.popX();
gdi.dropLine(1.2);
gdi.addButton("FTeam", "Tävling med lag", NewGuideCB);
gdi.addButton("FNoCoursesRelay", "Endast tidtagning (utan banor), stafett", NewGuideCB);
gdi.popX();
gdi.dropLine(3);
gdi.addString("", 1, "Övrigt").setColor(colorDarkBlue);
gdi.popX();
gdi.dropLine(1.2);
gdi.addButton("FAll", "Alla funktioner", NewGuideCB);
gdi.addButton("FSelect", "Välj från lista...", NewGuideCB);
gdi.addButton("Cancel", "Avbryt", NewGuideCB).setCancel();
if (oe->hasTeam()) {
gdi.disableInput("FIndividual");
gdi.disableInput("FForked");
gdi.disableInput("FBasic");
gdi.disableInput("FNoCourses");
}
gdi.popX();
gdi.fillDown();
gdi.dropLine(3);
rc.bottom = gdi.getCY();
gdi.dropLine();
gdi.addRectangle(rc, colorLightBlue, true);
gdi.refresh();
}
else if (step == 3) {
gdi.restore("entrychoice");
RECT rcl = rc;
rcl.top = gdi.getCY();
gdi.fillDown();
gdi.pushX();
meosFeatures(gdi, true);
rcl.bottom = gdi.getHeight();
if (gdi.getWidth() >= rc.right + gdi.scaleLength(30))
rcl.right = gdi.getWidth();
gdi.dropLine();
gdi.addRectangle(rcl, colorLightBlue, true);
gdi.popX();
gdi.dropLine();
gdi.setCY(gdi.getHeight());
gdi.fillRight();
gdi.addButton("NoEntries", "<< Bakåt", NewGuideCB);
gdi.addButton("StoreFeatures", "Skapa tävlingen", NewGuideCB);
gdi.addButton("Cancel", "Avbryt", NewGuideCB).setCancel();
gdi.popX();
gdi.dropLine(2);
gdi.fillDown();
gdi.refresh();
}
}
void TabCompetition::createNewCmp(gdioutput& gdi, bool useExisting) {
if (!useExisting)
createCompetition(gdi);
gdi.clearPage(true);
gdi.fillRight();
gdi.addString("", fontMediumPlus, "Skapar tävling...");
gdi.refresh();
gdi.setWaitCursor(true);
Sleep(400);
oe->updateTabs(true, false);
gdi.setWaitCursor(false);
loadPage(gdi);
}
void TabCompetition::entryChoice(gdioutput &gdi) {
gdi.fillRight();
gdi.pushX();
gdi.addButton("ImportEntries", "Importera anmälda", NewGuideCB);
//gdi.addButton("FreeEntry", "Fri inmatning av deltagare", NewGuideCB);
gdi.addButton("NoEntries", "Anmäl inga deltagare nu", NewGuideCB);
gdi.addButton("Cancel", "Avbryt", NewGuideCB).setCancel();
gdi.popX();
gdi.fillDown();
gdi.dropLine(2);
}
wstring getHiredCardDefault() {
wchar_t path[_MAX_PATH];
getUserFile(path, L"hired_card_default.csv");
return path;
}
void resetSaveTimer();
void TabCompetition::createCompetition(gdioutput &gdi) {
wstring name = gdi.getText("Name");
wstring date = gdi.getText("Date");
wstring start = gdi.getText("FirstStart");
int t = convertAbsoluteTimeHMS(start, -1);
if (t > 0 && t < timeConstHour * 24) {
t = max(0, t - timeConstHour);
}
else
throw meosException("Ogiltig tid");
int baseOnId = gdi.getSelectedItem("CmpSel").first;
if (baseOnId <= 0) {
oe->newCompetition(L"tmp");
oe->loadDefaults();
}
else {
openCompetition(gdi, baseOnId);
wstring cmpCopy = getTempFile();
oe->save(cmpCopy, false);
wstring rawName = oe->getName();
size_t posPar = rawName.find_first_of('(');
if (posPar != string::npos)
rawName = rawName.substr(0, posPar);
if (rawName.length() > 40)
rawName = trim(rawName.substr(0, 38)) + L"...";
wstring oldName = trim(rawName) + L" (" + oe->getDate() + L")";
oe->open(cmpCopy, true, false, true);
removeTempFile(cmpCopy);
oe->clearData(true, true);
oe->setAnnotation(lang.tl(L"Baserad på X#" + oldName));
resetSaveTimer();
}
auto manualName = !gdi.isChecked("UseFileEventName");
auto manualDate = !gdi.isChecked("UseFileEventDate");
oe->setName(name, manualName);
oe->setDate(date, manualDate);
oe->setZeroTime(formatTimeHMS(t), manualDate);
bool importHiredCard = true;
if (importHiredCard)
importDefaultHiredCards(gdi);
}
void TabCompetition::importDefaultHiredCards(gdioutput& gdi) {
csvparser csv;
list> data;
wstring fn = getHiredCardDefault();
if (fileExists(fn)) {
try {
csv.parse(fn, data);
set rentCards;
for (auto& c : data) {
for (wstring wc : c) {
int cn = _wtoi(wc.c_str());
if (cn > 0) {
oe->setHiredCard(cn, true);
}
}
}
}
catch (const meosException& ex) {
gdi.addString("", 0, ex.wwhat()).setColor(colorRed);
}
catch (std::exception& ex) {
gdi.addString("", 0, ex.what()).setColor(colorRed);
}
}
}