531 lines
15 KiB
C++
531 lines
15 KiB
C++
/************************************************************************
|
|
MeOS - Orienteering Software
|
|
Copyright (C) 2009-2019 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 <http://www.gnu.org/licenses/>.
|
|
|
|
Melin Software HB - software@melin.nu - www.melin.nu
|
|
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
|
|
|
|
************************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "resource.h"
|
|
|
|
#include <commctrl.h>
|
|
#include <commdlg.h>
|
|
#include <algorithm>
|
|
|
|
#include "oEvent.h"
|
|
#include "xmlparser.h"
|
|
#include "gdioutput.h"
|
|
#include "csvparser.h"
|
|
#include "SportIdent.h"
|
|
#include "meos_util.h"
|
|
#include "gdifonts.h"
|
|
#include "table.h"
|
|
#include <cassert>
|
|
#include "MeOSFeatures.h"
|
|
|
|
#include "TabControl.h"
|
|
|
|
TabControl::TabControl(oEvent *poe):TabBase(poe)
|
|
{
|
|
clearCompetitionData();
|
|
}
|
|
|
|
TabControl::~TabControl(void)
|
|
{
|
|
}
|
|
|
|
void TabControl::selectControl(gdioutput &gdi, pControl pc)
|
|
{
|
|
if (pc) {
|
|
pc->synchronize();
|
|
|
|
if (pc->getStatus() == oControl::StatusStart ||
|
|
pc->getStatus() == oControl::StatusFinish) {
|
|
gdi.selectItemByData("Controls", pc->getId());
|
|
gdi.selectItemByData("Status", oControl::StatusOK);
|
|
gdi.setText("ControlID", makeDash(L"-"), true);
|
|
|
|
gdi.setText("Code", L"");
|
|
gdi.setText("Name", pc->getName());
|
|
gdi.setText("TimeAdjust", L"00:00");
|
|
gdi.setText("MinTime", makeDash(L"-"));
|
|
gdi.setText("Point", L"");
|
|
controlId = pc->getId();
|
|
|
|
gdi.enableInput("Remove");
|
|
gdi.enableInput("Save");
|
|
gdi.enableEditControls(false);
|
|
gdi.enableInput("Name");
|
|
}
|
|
else {
|
|
gdi.selectItemByData("Controls", pc->getId());
|
|
gdi.selectItemByData("Status", pc->getStatus());
|
|
const int numVisit = pc->getNumVisitors(true);
|
|
const int numVisitExp = pc->getNumVisitors(false);
|
|
|
|
wstring info;
|
|
if (numVisit > 0) {
|
|
info = L"Antal besökare X, genomsnittlig bomtid Y, största bomtid Z#" +
|
|
itow(numVisit) + L" (" + itow(numVisitExp) + L")#" + getTimeMS(pc->getMissedTimeTotal() / numVisit) +
|
|
L"#" + getTimeMS(pc->getMissedTimeMax());
|
|
}
|
|
else if (numVisitExp > 0) {
|
|
info = L"Förväntat antal besökare: X#" + itow(numVisitExp);
|
|
}
|
|
gdi.setText("ControlID", itow(pc->getId()), true);
|
|
|
|
wstring winfo = lang.tl(info);
|
|
gdi.setText("Info", winfo, true);
|
|
gdi.setText("Code", pc->codeNumbers());
|
|
gdi.setText("Name", pc->getName());
|
|
gdi.setText("TimeAdjust", pc->getTimeAdjustS());
|
|
gdi.setText("MinTime", pc->getMinTimeS());
|
|
if (gdi.hasField("Point"))
|
|
gdi.setText("Point", pc->getRogainingPointsS());
|
|
|
|
controlId = pc->getId();
|
|
|
|
gdi.enableInput("Remove");
|
|
gdi.enableInput("Save");
|
|
gdi.enableInput("Visitors");
|
|
gdi.enableInput("Courses");
|
|
gdi.enableEditControls(true);
|
|
|
|
oControl::ControlStatus st = pc->getStatus();
|
|
if (st == oControl::StatusRogaining || st == oControl::StatusNoTiming)
|
|
gdi.disableInput("MinTime");
|
|
|
|
if (st == oControl::StatusNoTiming)
|
|
gdi.disableInput("TimeAdjust");
|
|
|
|
if (gdi.hasField("Point") && st != oControl::StatusRogaining)
|
|
gdi.disableInput("Point");
|
|
}
|
|
}
|
|
else {
|
|
gdi.selectItemByData("Controls", -1);
|
|
gdi.selectItemByData("Status", oControl::StatusOK);
|
|
gdi.setText("Code", L"");
|
|
gdi.setText("Name", L"");
|
|
controlId = 0;
|
|
|
|
gdi.setText("ControlID", makeDash(L"-"), true);
|
|
gdi.setText("TimeAdjust", L"00:00");
|
|
if (gdi.hasField("Point"))
|
|
gdi.setText("Point", L"");
|
|
|
|
gdi.disableInput("Remove");
|
|
gdi.disableInput("Save");
|
|
gdi.disableInput("Visitors");
|
|
gdi.disableInput("Courses");
|
|
|
|
gdi.enableEditControls(false);
|
|
}
|
|
}
|
|
|
|
int ControlsCB(gdioutput *gdi, int type, void *data)
|
|
{
|
|
TabControl &tc = dynamic_cast<TabControl &>(*gdi->getTabs().get(TControlTab));
|
|
return tc.controlCB(*gdi, type, data);
|
|
}
|
|
|
|
void TabControl::save(gdioutput &gdi)
|
|
{
|
|
if (controlId==0)
|
|
return;
|
|
|
|
DWORD pcid = controlId;
|
|
|
|
pControl pc;
|
|
pc = oe->getControl(pcid, false);
|
|
|
|
if (!pc)
|
|
throw std::exception("Internal error");
|
|
if (pc->getStatus() != oControl::StatusFinish && pc->getStatus() != oControl::StatusStart) {
|
|
if (!pc->setNumbers(gdi.getText("Code")))
|
|
gdi.alert("Kodsiffran måste vara ett heltal. Flera kodsiffror måste separeras med komma.");
|
|
|
|
pc->setStatus(oControl::ControlStatus(gdi.getSelectedItem("Status").first));
|
|
pc->setTimeAdjust(gdi.getText("TimeAdjust"));
|
|
if (pc->getStatus() != oControl::StatusRogaining) {
|
|
if (pc->getStatus() != oControl::StatusNoTiming)
|
|
pc->setMinTime(gdi.getText("MinTime"));
|
|
pc->setRogainingPoints(0);
|
|
}
|
|
else {
|
|
if (gdi.hasField("Point")) {
|
|
pc->setMinTime(0);
|
|
pc->setRogainingPoints(gdi.getTextNo("Point"));
|
|
}
|
|
}
|
|
}
|
|
|
|
pc->setName(gdi.getText("Name"));
|
|
|
|
pc->synchronize();
|
|
vector< pair<wstring, size_t> > d;
|
|
oe->fillControls(d, oEvent::CTAll);
|
|
gdi.addItem("Controls", d);
|
|
|
|
oe->reEvaluateAll(set<int>(), true);
|
|
|
|
selectControl(gdi, pc);
|
|
}
|
|
|
|
static void visitorTable(Table &table, void *ptr) {
|
|
TabControl *view = (TabControl *)ptr;
|
|
view->visitorTable(table);
|
|
}
|
|
|
|
static void courseTable(Table &table, void *ptr) {
|
|
TabControl *view = (TabControl *)ptr;
|
|
view->courseTable(table);
|
|
}
|
|
|
|
void TabControl::courseTable(Table &table) const {
|
|
vector<pRunner> r;
|
|
oe->getRunners(0, 0, r, false);
|
|
map<int, int> runnersPerCourse;
|
|
for (size_t k = 0; k < r.size(); k++) {
|
|
pCourse c = r[k]->getCourse(false);
|
|
int id = c != 0 ? c->getId() : 0;
|
|
++runnersPerCourse[id];
|
|
}
|
|
|
|
vector<pCourse> crs;
|
|
oe->getCourses(crs);
|
|
|
|
int ix = 1;
|
|
for (size_t k = 0; k < crs.size(); k++) {
|
|
vector<pControl> pc;
|
|
crs[k]->getControls(pc);
|
|
int used = 0;
|
|
for (size_t j = 0; j < pc.size(); j++) {
|
|
if (pc[j]->getId() == controlId) {
|
|
used++;
|
|
}
|
|
}
|
|
|
|
oCourse &it = *crs[k];
|
|
if (used > 0) {
|
|
table.addRow(ix++, &it);
|
|
|
|
int row = 0;
|
|
table.set(row++, it, TID_ID, itow(it.getId()), false);
|
|
table.set(row++, it, TID_MODIFIED, it.getTimeStamp(), false);
|
|
|
|
table.set(row++, it, TID_COURSE, crs[k]->getName(), false);
|
|
table.set(row++, it, TID_INDEX, itow(used), false);
|
|
table.set(row++, it, TID_RUNNER, itow(runnersPerCourse[crs[k]->getId()]), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabControl::visitorTable(Table &table) const {
|
|
vector<pCard> c;
|
|
oe->getCards(c);
|
|
pControl pc = oe->getControl(controlId, false);
|
|
|
|
if (!pc)
|
|
return;
|
|
vector<int> n;
|
|
pc->getNumbers(n);
|
|
struct PI {
|
|
PI(int c, int type, int t) : card(c), code(type), time(t) {}
|
|
int card;
|
|
int code;
|
|
int time;
|
|
bool operator<(const PI&c) const {
|
|
if (card != c.card)
|
|
return card < c.card;
|
|
else if (code != c.code)
|
|
return code < c.code;
|
|
else
|
|
return time < c.time;
|
|
}
|
|
bool operator==(const PI&c) const {
|
|
return c.card == card && c.code == code && c.time == time;
|
|
}
|
|
};
|
|
set<PI> registeredPunches;
|
|
vector<pPunch> p;
|
|
int ix=1;
|
|
for (size_t k = 0; k< c.size(); k++) {
|
|
oCard &it = *c[k];
|
|
|
|
it.getPunches(p);
|
|
//oPunch *punch = it.getPunchByType(pc->getFirstNumber()); //XXX
|
|
|
|
for (size_t j = 0; j < p.size(); j++) {
|
|
|
|
vector<int>::iterator res = find(n.begin(), n.end(), p[j]->getTypeCode());
|
|
if (res != n.end()) {
|
|
oPunch *punch = p[j];
|
|
registeredPunches.insert(PI(it.getCardNo(), p[j]->getTypeCode(), p[j]->getAdjustedTime()));
|
|
table.addRow(ix++, &it);
|
|
|
|
int row = 0;
|
|
table.set(row++, it, TID_ID, itow(it.getId()), false);
|
|
table.set(row++, it, TID_MODIFIED, it.getTimeStamp(), false);
|
|
|
|
pRunner r = it.getOwner();
|
|
if (r) {
|
|
table.set(row++, it, TID_RUNNER, r->getName(), false);
|
|
table.set(row++, it, TID_COURSE, r->getCourseName(), false);
|
|
}
|
|
else {
|
|
table.set(row++, it, TID_RUNNER, L"-", false);
|
|
table.set(row++, it, TID_COURSE, L"-", false);
|
|
}
|
|
table.set(row++, it, TID_FEE, punch->isUsedInCourse() ?
|
|
lang.tl("Ja") : lang.tl("Nej"), false);
|
|
table.set(row++, it, TID_CARD, it.getCardNoString(), false);
|
|
|
|
table.set(row++, it, TID_STATUS, punch->getTime(), false);
|
|
table.set(row++, it, TID_CONTROL, punch->getType(), false);
|
|
table.set(row++, it, TID_CODES, j>0 ? p[j-1]->getType() : L"-", true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int TabControl::controlCB(gdioutput &gdi, int type, void *data)
|
|
{
|
|
if (type==GUI_BUTTON) {
|
|
ButtonInfo bi=*(ButtonInfo *)data;
|
|
|
|
if (bi.id=="Save")
|
|
save(gdi);
|
|
else if (bi.id=="Add") {
|
|
bool rogaining = false;
|
|
if (controlId>0) {
|
|
save(gdi);
|
|
pControl pc = oe->getControl(controlId, false);
|
|
rogaining = pc && pc->getStatus() == oControl::StatusRogaining;
|
|
}
|
|
pControl pc = oe->addControl(0,oe->getNextControlNumber(), L"");
|
|
|
|
if (rogaining)
|
|
pc->setStatus(oControl::StatusRogaining);
|
|
vector< pair<wstring, size_t> > d;
|
|
oe->fillControls(d, oEvent::CTAll);
|
|
gdi.addItem("Controls", d);
|
|
selectControl(gdi, pc);
|
|
}
|
|
else if (bi.id=="Remove") {
|
|
|
|
DWORD cid = controlId;
|
|
if (cid==0) {
|
|
gdi.alert("Ingen kontroll vald vald.");
|
|
return 0;
|
|
}
|
|
|
|
if (oe->isControlUsed(cid))
|
|
gdi.alert("Kontrollen används och kan inte tas bort.");
|
|
else
|
|
oe->removeControl(cid);
|
|
|
|
vector< pair<wstring, size_t> > d;
|
|
oe->fillControls(d, oEvent::CTAll);
|
|
gdi.addItem("Controls", d);
|
|
selectControl(gdi, 0);
|
|
}
|
|
else if (bi.id=="SwitchMode") {
|
|
if (!tableMode)
|
|
save(gdi);
|
|
tableMode = !tableMode;
|
|
loadPage(gdi);
|
|
}
|
|
else if (bi.id=="Visitors") {
|
|
save(gdi);
|
|
|
|
Table *table=new Table(oe, 20, L"Kontroll X#" + itow(controlId), "controlvisitor");
|
|
|
|
table->addColumn("Id", 70, true, true);
|
|
table->addColumn("Ändrad", 70, false);
|
|
|
|
table->addColumn("Namn", 150, false);
|
|
table->addColumn("Bana", 70, false);
|
|
table->addColumn("På banan", 70, false);
|
|
table->addColumn("Bricka", 70, true, true);
|
|
|
|
table->addColumn("Tidpunkt", 70, false);
|
|
table->addColumn("Stämpelkod", 70, true);
|
|
table->addColumn("Föregående kontroll", 70, false);
|
|
table->setGenerator(::visitorTable, this);
|
|
table->setTableProp(0);
|
|
table->update();
|
|
gdi.clearPage(false);
|
|
int xp=gdi.getCX();
|
|
gdi.fillDown();
|
|
gdi.addButton("Show", "Återgå", ControlsCB);
|
|
gdi.addTable(table, xp, gdi.getCY());
|
|
gdi.refresh();
|
|
}
|
|
else if (bi.id=="Courses") {
|
|
Table *table=new Table(oe, 20, L"Kontroll X#" + itow(controlId), "controlcourse");
|
|
|
|
table->addColumn("Id", 70, true, true);
|
|
table->addColumn("Ändrad", 70, false);
|
|
|
|
table->addColumn("Bana", 70, false, true);
|
|
table->addColumn("Förekomst", 70, true, true);
|
|
table->addColumn("Antal deltagare", 70, true, true);
|
|
|
|
table->setGenerator(::courseTable, this);
|
|
table->setTableProp(0);
|
|
table->update();
|
|
gdi.clearPage(false);
|
|
int xp=gdi.getCX();
|
|
gdi.fillDown();
|
|
gdi.addButton("Show", "Återgå", ControlsCB);
|
|
gdi.addTable(table, xp, gdi.getCY());
|
|
gdi.refresh();
|
|
}
|
|
else if (bi.id=="Show") {
|
|
loadPage(gdi);
|
|
}
|
|
}
|
|
else if (type==GUI_LISTBOX){
|
|
ListBoxInfo bi=*(ListBoxInfo *)data;
|
|
|
|
if (bi.id=="Controls") {
|
|
if (gdi.isInputChanged(""))
|
|
save(gdi);
|
|
|
|
pControl pc=oe->getControl(bi.data, false);
|
|
if (!pc)
|
|
throw std::exception("Internal error");
|
|
|
|
selectControl(gdi, pc);
|
|
}
|
|
else if (bi.id == "Status" ) {
|
|
gdi.setInputStatus("MinTime", bi.data != oControl::StatusRogaining && bi.data != oControl::StatusNoTiming, true);
|
|
gdi.setInputStatus("Point", bi.data == oControl::StatusRogaining, true);
|
|
gdi.setInputStatus("TimeAdjust", bi.data != oControl::StatusNoTiming, true);
|
|
}
|
|
}
|
|
else if (type==GUI_CLEAR) {
|
|
if (controlId>0)
|
|
save(gdi);
|
|
|
|
return true;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool TabControl::loadPage(gdioutput &gdi)
|
|
{
|
|
oe->checkDB();
|
|
gdi.selectTab(tabId);
|
|
gdi.clearPage(false);
|
|
int xp=gdi.getCX();
|
|
|
|
const int button_w=gdi.scaleLength(90);
|
|
string switchMode;
|
|
switchMode=tableMode ? "Formulärläge" : "Tabelläge";
|
|
gdi.addButton(2, 2, button_w, "SwitchMode", switchMode,
|
|
ControlsCB, "Välj vy", false, false).fixedCorner();
|
|
|
|
if (tableMode) {
|
|
Table *tbl=oe->getControlTB();
|
|
gdi.addTable(tbl, xp, 30);
|
|
return true;
|
|
}
|
|
|
|
gdi.fillDown();
|
|
gdi.addString("", boldLarge, "Kontroller");
|
|
|
|
gdi.pushY();
|
|
gdi.addListBox("Controls", 250, 530, ControlsCB).isEdit(false).ignore(true);
|
|
gdi.setTabStops("Controls", 40, 160);
|
|
|
|
vector< pair<wstring, size_t> > d;
|
|
oe->fillControls(d, oEvent::CTAll);
|
|
gdi.addItem("Controls", d);
|
|
|
|
gdi.newColumn();
|
|
gdi.fillDown();
|
|
gdi.popY();
|
|
|
|
gdi.pushX();
|
|
gdi.fillRight();
|
|
gdi.addString("", 1, "Kontrollens ID-nummer:");
|
|
gdi.fillDown();
|
|
gdi.addString("ControlID", 1, "#-").setColor(colorGreen);
|
|
gdi.popX();
|
|
|
|
gdi.fillRight();
|
|
gdi.addInput("Name", L"", 16, 0, L"Kontrollnamn:");
|
|
|
|
gdi.addSelection("Status", 150, 100, ControlsCB, L"Status:", L"Ange om kontrollen fungerar och hur den ska användas.");
|
|
oe->fillControlStatus(gdi, "Status");
|
|
|
|
gdi.addInput("TimeAdjust", L"", 6, 0, L"Tidsjustering:");
|
|
gdi.fillDown();
|
|
gdi.addInput("MinTime", L"", 6, 0, L"Minsta sträcktid:");
|
|
|
|
gdi.popX();
|
|
gdi.dropLine(0.5);
|
|
gdi.addString("", 10, "help:9373");
|
|
|
|
gdi.fillRight();
|
|
|
|
gdi.dropLine(0.5);
|
|
gdi.addInput("Code", L"", 16, 0, L"Stämpelkod(er):");
|
|
|
|
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Rogaining)) {
|
|
gdi.addInput("Point", L"", 6, 0, L"Rogaining-poäng:");
|
|
}
|
|
gdi.popX();
|
|
gdi.dropLine(3.5);
|
|
|
|
gdi.fillRight();
|
|
gdi.addButton("Save", "Spara", ControlsCB, "help:save");
|
|
gdi.disableInput("Save");
|
|
gdi.addButton("Remove", "Radera", ControlsCB);
|
|
gdi.disableInput("Remove");
|
|
gdi.addButton("Courses", "Banor...", ControlsCB);
|
|
gdi.addButton("Visitors", "Besökare...", ControlsCB);
|
|
gdi.addButton("Add", "Ny kontroll", ControlsCB);
|
|
|
|
gdi.dropLine(2.5);
|
|
gdi.popX();
|
|
|
|
gdi.fillDown();
|
|
|
|
gdi.addString("Info", 0, "").setColor(colorGreen);
|
|
|
|
gdi.dropLine(1.5);
|
|
gdi.addString("", 10, "help:89064");
|
|
|
|
selectControl(gdi, oe->getControl(controlId, false));
|
|
|
|
gdi.setOnClearCb(ControlsCB);
|
|
|
|
gdi.refresh();
|
|
return true;
|
|
}
|
|
|
|
void TabControl::clearCompetitionData() {
|
|
tableMode = false;
|
|
controlId = 0;
|
|
}
|