meos-2024/code/methodeditor.cpp
2020-01-26 21:18:49 +01:00

1149 lines
36 KiB
C++

/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2020 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 <algorithm>
#include "methodeditor.h"
#include "generalresult.h"
#include "gdioutput.h"
#include "meosexception.h"
#include "gdistructures.h"
#include "meos_util.h"
#include "localizer.h"
#include "gdifonts.h"
#include "oEvent.h"
#include "tabbase.h"
#include "CommDlg.h"
#include "random.h"
#include "metalist.h"
MethodEditor::MethodEditor(oEvent *oe_) {
oe = oe_;
inputNumber = 0;
currentResult = 0;
currentIndex = -1;
dirtyInt = false;
wasLoadedBuiltIn = false;
}
MethodEditor::~MethodEditor() {
setCurrentResult(0, L"");
}
void MethodEditor::setCurrentResult(DynamicResult *lst, const wstring &fileSrc) {
delete currentResult;
currentResult = lst;
fileNameSource = fileSrc;
}
int methodCB(gdioutput *gdi, int type, void *data) {
void *clz = gdi->getData("MethodEditorClz");
MethodEditor *le = (MethodEditor *)clz;
BaseInfo *bi = (BaseInfo *)data;
if (le)
return le->methodCb(*gdi, type, *bi);
throw meosException("Unexpected error");
}
void MethodEditor::show(TabBase *dst, gdioutput &gdi) {
gdi.clearPage(false);
origin = dst;
show(gdi);
}
void MethodEditor::show(gdioutput &gdi) {
oe->loadGeneralResults(true, true);
gdi.setRestorePoint("BeginMethodEdit");
gdi.pushX();
gdi.setCX(gdi.getCX() + gdi.scaleLength(6));
int bx = gdi.getCX();
int by = gdi.getCY();
if (currentResult)
gdi.addString("", boldLarge, makeDash(L"Result Module - X#") + currentResult->getName(true));
else
gdi.addString("", boldLarge, "Edit Result Modules");
gdi.setOnClearCb(methodCB);
gdi.setData("MethodEditorClz", this);
gdi.dropLine(0.5);
gdi.fillRight();
gdi.addButton("OpenFile", "Importera från fil", methodCB);
gdi.addButton("OpenInside", "Öppna", methodCB);
if (currentResult) {
gdi.addButton("SaveFile", "Exportera till fil...", methodCB);
gdi.addButton("SaveInside", "Spara", methodCB);
gdi.addButton("Remove", "Ta bort", methodCB);
makeDirty(gdi, NoTouch);
}
gdi.addButton("NewRules", "New Result Module", methodCB);
gdi.addButton("Close", "Stäng", methodCB);
#ifdef _DEBUG
gdi.addButton("WriteDoc", "#WriteDoc", methodCB);
#endif
gdi.dropLine(2);
gdi.fillDown();
int dx = gdi.getCX();
int dy = gdi.getCY();
RECT rc;
int off = gdi.scaleLength(6);
rc.left = bx - 2 * off;
rc.right = dx + 2 * off;
rc.top = by - off;
rc.bottom = dy + off;
gdi.addRectangle(rc, colorWindowBar);
gdi.popX();
if (currentResult) {
gdi.dropLine(0.5);
if (currentResult->getTag().empty())
currentResult->setTag(uniqueTag("result"));
gdi.addInput("Name", currentResult->getName(false), 20, methodCB, L"Name of result module:");
string tag = currentResult->getTag();
vector<int> listIx;
oe->getListContainer().getListsByResultModule(tag, listIx);
string udtag = DynamicResult::undecorateTag(tag);
gdi.addInput("Tag", gdi.widen(udtag), 20, methodCB, L"Result module identifier:");
if (!listIx.empty()) {
gdi.disableInput("Tag");
gdi.getBaseInfo("Tag").setExtra(1);
wstring lists = oe->getListContainer().getList(listIx.front()).getListName();
if (listIx.size() > 1)
lists += L", ...";
gdi.addString("", 0, L"Resultatmodulen används i X.#" + lists);
}
wstring desc = currentResult->getDescription();
if (wasLoadedBuiltIn)
desc = lang.tl(desc);
gdi.addInputBox("Desc", 300, 70, desc, methodCB, L"Description:");
gdi.dropLine();
gdi.fillRight();
gdi.addSelection("Method", 200, 200, methodCB, L"Edit rule for:");
vector< pair<DynamicResult::DynamicMethods, string> > mt;
currentResult->getMethodTypes(mt);
for (size_t k = 0; k < mt.size(); k++)
gdi.addItem("Method", lang.tl(mt[k].second), mt[k].first);
gdi.dropLine();
gdi.addButton("SaveSource", "Save changes", methodCB);
gdi.addButton("CancelSource", "Cancel", methodCB);
gdi.addButton("TestRule", "Test Result Module", methodCB);
gdi.disableInput("SaveSource");
gdi.disableInput("CancelSource");
gdi.dropLine(2);
gdi.popX();
gdi.fillDown();
gdi.setRestorePoint("TestRule");
}
gdi.refresh();
}
bool MethodEditor::checkTag(const string &tag, bool throwError) const {
vector< pair<int, pair<string, wstring> > > tagNameList;
oe->getGeneralResults(false, tagNameList, false);
for (size_t k = 0; k < tag.length(); k++) {
char c = tag[k];
if (c == '-' || c == '_' || c == '%')
continue;
if (!isalnum(c)) {
if (throwError)
throw meosException("Invalid character in tag X.#" + tag);
else
return false;
}
}
if (tag.empty()) {
if (throwError)
throw meosException("Empty tag is invalid");
else
return false;
}
for (size_t k = 0; k < tagNameList.size(); k++) {
if (_strcmpi(tagNameList[k].second.first.c_str(), tag.c_str()) == 0) {
if (throwError)
throw meosException("The tag X is in use.#" + tag);
else
return false;
}
}
return true;
}
string MethodEditor::uniqueTag(const string &tag) const {
vector< pair<int, pair<string, wstring> > > tagNameList;
oe->getGeneralResults(false, tagNameList, false);
set<string> tags;
for (size_t k = 0; k < tagNameList.size(); k++)
tags.insert(tagNameList[k].second.first);
int a = GetRandomNumber(65536);
srand(GetTickCount());
int b = rand() % 65536;
char un[64];
sprintf_s(un, "-%04X-%04X-", a, b);
int iter = 0;
while (tags.count(tag + un + itos(++iter)));
return tag + un + itos(iter);
}
void makeScore(wstring &str, const pair<int, int> &score) {
int64_t v = int64_t(score.first) * (1LL << 32) + int64_t(score.second);
str = itow(v);
}
int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) {
if (type == GUI_BUTTON) {
ButtonInfo bi = dynamic_cast<ButtonInfo &>(data);
if (bi.id == "Cancel") {
gdi.restore("EditList");
gdi.enableInput("EditList");
}
else if (bi.id == "CloseWindow") {
gdi.closeWindow();
}
else if (bi.id == "WriteDoc") {
ofstream fout("methoddoc.txt");
DynamicResult dr;
dr.declareSymbols(DynamicResult::MRScore, true);
vector< pair<wstring, size_t> > symbs;
dr.getSymbols(symbs);
fout << "#head{" << gdi.toUTF8(lang.tl("Deltagare")) << "}\n#table{2}" << endl;
for (size_t k = 0; k < symbs.size(); k++) {
wstring name, desc;
dr.getSymbolInfo(symbs[k].second, name, desc);
fout << "{#mono{" << gdi.toUTF8(name) << "}}{" << gdi.toUTF8(lang.tl(desc)) << "}" << endl;
}
dr.declareSymbols(DynamicResult::MTScore, true);
dr.getSymbols(symbs);
fout << "#head{" << gdi.toUTF8(lang.tl("Lag")) << "}\n#table{2}" << endl;
for (size_t k = 0; k < symbs.size(); k++) {
wstring name, desc;
dr.getSymbolInfo(symbs[k].second, name, desc);
fout << "{#mono{" << gdi.toUTF8(name) << "}}{" << gdi.toUTF8(lang.tl(desc)) << "}" << endl;
}
}
else if (bi.id == "NewRules") {
if (!checkSave(gdi))
return 0;
gdi.clearPage(false);
gdi.setData("MethodEditorClz", this);
gdi.addString("", boldLarge, "New Set of Result Rules");
setCurrentResult(new DynamicResult(), L"");
wasLoadedBuiltIn = false;
currentResult->setName(lang.tl("Result Calculation"));
currentIndex = -1;
makeDirty(gdi, ClearDirty);
show(gdi);
gdi.setInputStatus("Remove", resultIsInstalled());
}
else if (bi.id == "SaveFile") {
if (!currentResult)
return 0;
checkChangedSave(gdi);
wstring fileName;
int ix = 0;
vector< pair<wstring, wstring> > ext;
ext.push_back(make_pair(L"xml-data", L"*.rules"));
fileName = gdi.browseForSave(ext, L"rules", ix);
if (fileName.empty())
return 0;
saveSettings(gdi);
wstring path = fileNameSource.empty() ? getInternalPath(currentResult->getTag()) : fileNameSource;
currentResult->save(path);
fileNameSource = path;
oe->loadGeneralResults(true, true);
makeDirty(gdi, ClearDirty);
currentResult->save(fileName);
//savedFileName = fileName;
return 1;
}
else if (bi.id == "SaveInside") {
if (!currentResult)
return 0;
saveSettings(gdi);
checkChangedSave(gdi);
wstring path = fileNameSource.empty() ? getInternalPath(currentResult->getTag()) : fileNameSource;
currentResult->save(path);
fileNameSource = path;
int nl = oe->getListContainer().getNumLists();
string utag = DynamicResult::undecorateTag(currentResult->getTag());
bool doUpdate = false;
for (int j = 0; j < nl && !doUpdate; j++) {
if (!oe->getListContainer().isExternal(j))
continue;
const string &mtag = oe->getListContainer().getList(j).getResultModule();
if (mtag.empty() || currentResult->getTag() == mtag)
continue;
if (utag == DynamicResult::undecorateTag(mtag)) {
doUpdate = gdi.ask(L"Vill du uppdatera resultatlistorna i den öppande tävlingen?");
break;
}
}
oe->synchronize(false);
oe->getListContainer().updateResultModule(*currentResult, doUpdate);
oe->loadGeneralResults(true, true);
oe->synchronize(true);
makeDirty(gdi, ClearDirty);
gdi.setInputStatus("Remove", resultIsInstalled());
return 1;
}
else if (bi.id == "Remove") {
if (!currentResult)
return 0;
if (gdi.ask(L"Vill du ta bort 'X'?#" + currentResult->getName(true))) {
wstring path = fileNameSource;//getInternalPath(currentResult->getTag());
wstring rm = path + L".removed";
DeleteFile(rm.c_str());
_wrename(path.c_str(), rm.c_str());
oe->loadGeneralResults(true, true);
makeDirty(gdi, ClearDirty);
setCurrentResult(0, L"");
gdi.clearPage(false);
show(gdi);
}
return 1;
}
else if (bi.id == "OpenFile") {
if (!checkSave(gdi))
return 0;
vector< pair<wstring, wstring> > ext;
ext.push_back(make_pair(L"xml-data", L"*.rules"));
wstring fileName = gdi.browseForOpen(ext, L"rules");
if (fileName.empty())
return 0;
DynamicResult *tmp = new DynamicResult();
try {
tmp->load(fileName);
}
catch(...) {
delete tmp;
throw;
}
wasLoadedBuiltIn = false;
setCurrentResult(tmp, L"");
currentIndex = -1;
gdi.clearPage(false);
//savedFileName = fileName;
makeDirty(gdi, ClearDirty);
show(gdi);
}
else if (bi.id == "OpenInside") {
if (!checkSave(gdi))
return 0;
gdi.clearPage(true);
gdi.setOnClearCb(methodCB);
gdi.setData("MethodEditorClz", this);
gdi.pushX();
vector< pair<wstring, size_t> > lists;
//oe->getListContainer().getLists(lists);
vector< pair<int, pair<string, wstring> > > tagNameList;
oe->getGeneralResults(true, tagNameList, true);
map<string, wstring> tag2ClassUsage;
vector<pClass> cls;
oe->getClasses(cls, false);
for (pClass c : cls) {
string rt = c->getResultModuleTag();
if (!rt.empty()) {
wstring &n = tag2ClassUsage[rt];
if (n.empty())
n = L" | " + c->getName();
else
n += L", \x2026";
}
}
for (size_t k = 0; k < tagNameList.size(); k++) {
string tag = tagNameList[k].second.first;
string utag = DynamicResult::undecorateTag(tag);
vector<int> listIx;
oe->getListContainer().getListsByResultModule(tag, listIx);
wstring n = tagNameList[k].second.second + L" (" + gdi.widen(utag) + L")";
if (listIx.size() > 0) {
n += L" *";
}
else if (tag2ClassUsage.count(tag))
n += tag2ClassUsage[tag];
lists.push_back(make_pair(n, tagNameList[k].first));
}
sort(lists.begin(), lists.end());
gdi.fillRight();
gdi.addSelection("OpenList", 350, 400, methodCB, L"Choose result module:", L"Rader markerade med (*) kommer från en lista i tävlingen.");
gdi.addItem("OpenList", lists);
gdi.autoGrow("OpenList");
gdi.selectFirstItem("OpenList");
gdi.dropLine();
gdi.addButton("DoOpen", "Öppna", methodCB);
gdi.addButton("DoOpenCopy", "Open a Copy", methodCB);
if (!lists.empty()) {
wstring srcFile;
bool ro = dynamic_cast<DynamicResult &>(*oe->getGeneralResult(tagNameList.front().second.first, srcFile)).isReadOnly();
gdi.setInputStatus("DoOpen", !ro);
}
else {
gdi.disableInput("DoOpen");
gdi.disableInput("DoOpenCopy");
}
gdi.addButton("CancelReload", "Avbryt", methodCB);
gdi.dropLine(4);
gdi.popX();
}
else if (bi.id == "DoOpen" || bi.id == "DoOpenCopy") {
ListBoxInfo lbi;
DynamicResult *dr = 0;
if (gdi.getSelectedItem("OpenList", lbi)) {
vector< pair<int, pair<string, wstring> > > tagNameList;
oe->getGeneralResults(true, tagNameList, false);
size_t ix = -1;
for (size_t k = 0; k < tagNameList.size(); k++) {
if (tagNameList[k].first == lbi.data) {
ix = k;
break;
}
}
if (ix < tagNameList.size()) {
dr = load(gdi, tagNameList[ix].second.first, bi.id == "DoOpenCopy");
/*wstring srcFile;
DynamicResult &drIn = dynamic_cast<DynamicResult &>(*oe->getGeneralResult(tagNameList[ix].second.first, srcFile));
wasLoadedBuiltIn = drIn.isReadOnly();
dr = new DynamicResult(drIn);
if (bi.id == "DoOpenCopy") {
dr->setTag(uniqueTag("result"));
dr->setName(lang.tl("Copy of ") + dr->getName(false));
setCurrentResult(dr, L"");
}
else
setCurrentResult(dr, srcFile);*/
}
}
}
else if (bi.id == "CancelReload") {
gdi.clearPage(false);
show(gdi);
}
else if (bi.id == "Close") {
if (!checkSave(gdi))
return 0;
setCurrentResult(0, L"");
makeDirty(gdi, ClearDirty);
currentIndex = -1;
if (origin) {
auto oc = origin;
origin = nullptr;
oc->loadPage(gdi);
}
return 0;
}
else if (bi.id == "SaveSource") {
DynamicResult::DynamicMethods dm = DynamicResult::DynamicMethods(bi.getExtraInt());
string src = gdi.narrow(gdi.getText("Source"));
currentResult->setMethodSource(dm, src);
gdi.setText("Source", gdi.widen(src));
}
else if (bi.id == "CancelSource") {
checkChangedSave(gdi);
gdi.restore("NoSourceEdit", true);
gdi.selectItemByData("Method", -1);
gdi.disableInput("SaveSource");
gdi.disableInput("CancelSource");
}
else if (bi.id == "TestRule") {
checkChangedSave(gdi);
gdi.restore("TestRule", false);
gdi.setRestorePoint("TestRule");
gdi.disableInput("SaveSource");
gdi.disableInput("CancelSource");
gdi.selectItemByData("Method", -1);
int y = gdi.getCY();
vector<pRunner> r;
vector<pTeam> t;
oe->getRunners(0, 0, r, true);
oe->getTeams(0, t, true);
vector<pRunner> rr;
vector<pTeam> tr;
for (size_t k = 0; k < r.size(); k++) {
if (r[k]->getStatus() != StatusUnknown)
rr.push_back(r[k]);
}
for (size_t k = 0; k < t.size(); k++) {
if (t[k]->getStatus() != StatusUnknown)
tr.push_back(t[k]);
else {
for (int j = 0; j < t[k]->getNumRunners(); j++) {
if (t[k]->getRunner(j) && t[k]->getRunner(j)->getStatus() != StatusUnknown) {
tr.push_back(t[k]);
break;
}
}
}
}
gdi.fillDown();
if (tr.size() + rr.size() == 0) {
gdi.addString("", 1, "Tävlingen innehåller inga resultat").setColor(colorRed);
}
else
gdi.addString("", 1, "Applying rules to the current competition");
gdi.dropLine(0.5);
int xp = gdi.getCX();
int yp = gdi.getCY();
int diff = gdi.scaleLength(3);
int w[5] = {200, 70, 70, 70, 135};
for (int i = 0; i < 5; i++)
w[i] = gdi.scaleLength(w[i]);
set<wstring> errors;
currentResult->prepareCalculations(*oe, false, {}, rr, tr, inputNumber);
for (size_t k = 0; k < rr.size(); k++) {
int txp = xp;
int wi = 0;
gdi.addStringUT(yp, txp, 0, rr[k]->getCompleteIdentification(), w[wi]-diff);
txp += w[wi++];
currentResult->prepareCalculations(*rr[k], false);
int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown;
{
wstring err;
wstring str;
try {
st = currentResult->deduceStatus(*rr[k]);
str = oe->formatStatus(st, false);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
RECT rc = ti.textRect;
gdi.addToolTip("", err, 0, &rc);
}
txp += w[wi++];
}
{
wstring err;
wstring str;
try {
rt = currentResult->deduceTime(*rr[k], rr[k]->getStartTime());
str = formatTime(rt);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
{
wstring err;
wstring str;
try {
pt = currentResult->deducePoints(*rr[k]);
str = itow(pt);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
{
wstring err;
wstring str;
try {
pair<int,int> score = currentResult->score(*rr[k], st, rt, pt, false);
makeScore(str, score);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
rr[k]->setTempResultZero(oAbstractRunner::TempResult(st, rr[k]->getStartTime(), rt, pt));
gdi.addString("Debug", yp, txp, 0, "Debug...", 0, methodCB).setColor(colorGreen).setExtra(rr[k]->getId());
yp += gdi.getLineHeight();
}
yp += gdi.getLineHeight();
if (tr.size() > 0) {
gdi.addString("", yp, xp, 1, "Lag");
yp += gdi.getLineHeight();
}
else {
}
for (size_t k = 0; k < tr.size(); k++) {
int txp = xp;
int wi = 0;
gdi.addStringUT(yp, txp, 0, tr[k]->getName(), w[wi]-diff);
txp += w[wi++];
currentResult->prepareCalculations(*tr[k], false);
int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown;
{
wstring err;
wstring str;
try {
st = currentResult->deduceStatus(*tr[k]);
str = oe->formatStatus(st, false);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
{
wstring err;
wstring str;
try {
rt = currentResult->deduceTime(*tr[k]);
str = formatTime(rt);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
{
wstring err;
wstring str;
try {
pt = currentResult->deducePoints(*tr[k]);
str = itow(pt);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
{
wstring err;
wstring str;
try {
auto score = currentResult->score(*tr[k], st, rt, pt);
makeScore(str, score);
}
catch (meosException &ex) {
err = ex.wwhat();
errors.insert(ex.wwhat());
str = L"Error";
}
TextInfo &ti = gdi.addStringUT(yp, txp, 0, str, w[wi]-diff);
if (!err.empty()) {
ti.setColor(colorRed);
gdi.addToolTip("", err, 0, &ti.textRect);
}
txp += w[wi++];
}
gdi.addString("Debug", yp, txp, 0, "Debug...", 0, methodCB).setColor(colorGreen).setExtra(-tr[k]->getId());
yp += gdi.getLineHeight();
}
gdi.scrollTo(0, y);
gdi.refresh();
}
}
else if (type == GUI_LISTBOX) {
ListBoxInfo &lbi = dynamic_cast<ListBoxInfo &>(data);
if (lbi.id == "Method") {
checkChangedSave(gdi);
DynamicResult::DynamicMethods m = DynamicResult::DynamicMethods(lbi.data);
const string &src = currentResult->getMethodSource(m);
gdi.enableInput("SaveSource");
gdi.enableInput("CancelSource");
gdi.getBaseInfo("SaveSource").setExtra(lbi.data);
if (!gdi.hasWidget("Source")) {
gdi.fillRight();
gdi.restore("TestRule", false);
gdi.pushX();
gdi.setRestorePoint("NoSourceEdit");
gdi.addInputBox("Source", 450, 300,
gdi.widen(src),
methodCB, L"Source code:").setFont(gdi, monoText);
gdi.fillDown();
gdi.setCX(gdi.getCX() + gdi.getLineHeight());
gdi.addListBox("Symbols", 600, 300-20, methodCB, L"Available symbols:");
gdi.setTabStops("Symbols", 250);
gdi.addString("SymbInfo", gdi.getCY(), gdi.getCX(), 0, "", 350, 0);
gdi.popX();
gdi.setCY(gdi.getHeight());
gdi.dropLine();
gdi.scrollToBottom();
}
else {
gdi.setText("Source", gdi.widen(src));
}
currentResult->declareSymbols(m, true);
vector< pair<wstring, size_t> > symb;
currentResult->getSymbols(symb);
gdi.addItem("Symbols", symb);
}
else if (lbi.id == "Symbols") {
wstring name, desc;
currentResult->getSymbolInfo(lbi.data, name, desc);
gdi.setText("SymbInfo", name + L":" + lang.tl(desc) + L".", true);
}
else if (lbi.id == "OpenList") {
vector< pair<int, pair<string, wstring> > > tagNameList;
oe->getGeneralResults(true, tagNameList, false);
size_t ix = -1;
for (size_t k = 0; k < tagNameList.size(); k++) {
if (tagNameList[k].first == lbi.data) {
ix = k;
break;
}
}
wstring srcFile;
if (ix < tagNameList.size()) {
bool ro = dynamic_cast<DynamicResult &>(*oe->getGeneralResult(tagNameList[ix].second.first, srcFile)).isReadOnly();
gdi.setInputStatus("DoOpen", !ro);
}
else
gdi.setInputStatus("DoOpen", true);
}
}
else if (type == GUI_LISTBOXSELECT) {
ListBoxInfo &lbi = dynamic_cast<ListBoxInfo &>(data);
if (lbi.id == "Symbols") {
wstring name, desc;
currentResult->getSymbolInfo(lbi.data, name, desc);
gdi.replaceSelection("Source", name);
}
}
else if (type==GUI_CLEAR) {
return checkSave(gdi);
}
else if (type == GUI_INPUT) {
InputInfo &ii = dynamic_cast<InputInfo &>(data);
if (ii.changed())
makeDirty(gdi, MakeDirty);
}
else if (type == GUI_LINK) {
TextInfo &ti = dynamic_cast<TextInfo &>(data);
if (ti.id == "Debug") {
int id = ti.getExtraInt();
if (id > 0) {
debug(gdi, id, false);
}
else if (id < 0) {
debug(gdi, -id, true);
}
}
}
return 0;
}
void MethodEditor::saveSettings(gdioutput &gdi) {
wstring name = gdi.getText("Name");
string tag;
const bool updateTag = gdi.getBaseInfo("Tag").getExtraInt() == 0;
if (updateTag)
tag = gdi.narrow(gdi.getText("Tag"));
else
tag = currentResult->getTag();
wstring desc = gdi.getText("Desc");
if (_strcmpi(currentResult->getTag().c_str(), tag.c_str()) != 0) {
checkTag(tag, true);
wstring oldPath = fileNameSource.empty() ? getInternalPath(currentResult->getTag()) : fileNameSource;
wstring path = getInternalPath(tag);
_wrename(oldPath.c_str(), path.c_str());
fileNameSource = path;
}
currentResult->setTag(tag);
currentResult->setName(name);
currentResult->setDescription(desc);
gdi.setText("Name", name);
gdi.setText("Tag", gdi.widen(tag));
gdi.setText("Desc", desc);
}
void MethodEditor::checkUnsaved(gdioutput &gdi) {
/*if (gdi.hasData("IsEditing")) {
if (gdi.isInputChanged("")) {
gdi.setData("NoRedraw", 1);
gdi.sendCtrlMessage("Apply");
}
}
if (gdi.hasData("IsEditingList")) {
if (gdi.isInputChanged("")) {
gdi.setData("NoRedraw", 1);
gdi.sendCtrlMessage("ApplyListProp");
}
}*/
}
void MethodEditor::makeDirty(gdioutput &gdi, DirtyFlag inside) {
if (inside == MakeDirty)
dirtyInt = true;
else if (inside == ClearDirty)
dirtyInt = false;
if (gdi.hasWidget("SaveInside")) {
gdi.setInputStatus("SaveInside", dirtyInt);
gdi.setInputStatus("Remove", resultIsInstalled());
}
}
bool MethodEditor::checkSave(gdioutput &gdi) {
if (dirtyInt) {
gdioutput::AskAnswer answer = gdi.askCancel(L"Vill du spara ändringar?");
if (answer == gdioutput::AnswerCancel)
return false;
if (answer == gdioutput::AnswerYes) {
gdi.sendCtrlMessage("SaveInside");
}
makeDirty(gdi, ClearDirty);
}
return true;
}
void MethodEditor::checkChangedSave(gdioutput &gdi) {
if (gdi.hasWidget("Source")) {
gdi.getText("Source");
if (dynamic_cast<InputInfo &>(gdi.getBaseInfo("Source")).changed() &&
gdi.ask(L"Save changes in rule code?")) {
DynamicResult::DynamicMethods dm = DynamicResult::DynamicMethods(gdi.getExtraInt("SaveSource"));
string src = gdi.narrow(gdi.getText("Source"));
currentResult->setMethodSource(dm, src);
gdi.setText("Source", gdi.widen(src));
}
}
}
extern gdioutput *gdi_main;
wstring MethodEditor::getInternalPath(const string &tag) {
string udTag = DynamicResult::undecorateTag(tag);
wstring resFile;
if (udTag == tag)
resFile = gdi_main->widen(tag) + L".rules";
else
resFile = L"imp_" + gdi_main->widen(udTag) + L".rules";
wchar_t path[260];
getUserFile(path, resFile.c_str());
return path;
}
bool MethodEditor::resultIsInstalled() const {
if (!currentResult || fileNameSource.empty())
return false;
string tag = currentResult->getTag();
vector<int> listIx;
oe->getListContainer().getListsByResultModule(tag, listIx);
if (!listIx.empty())
return false; // Used in a list in this competition
//string path = getInternalPath(currentResult->getTag());
return fileExist(fileNameSource.c_str());
}
void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) {
oAbstractRunner *art = 0;
if (isTeam)
art = oe->getTeam(id);
else
art = oe->getRunner(id, 0);
if (!art || !currentResult)
throw meosException("Internal error");
gdioutput *gdi_new = getExtraWindow("debug", true);
wstring title = lang.tl(L"Debug X for Y#" + currentResult->getName(false) + L"#" + art->getName());
if (!gdi_new)
gdi_new = createExtraWindow("debug", title,
gdi_in.scaleLength(500) );
else
gdi_new->setWindowTitle(title);
gdi_new->clearPage(false);
gdi_new->setData("MethodEditorClz", this);
gdioutput &gdi = *gdi_new;
vector<pRunner> rr;
vector<pTeam> tt;
oe->getRunners(art->getClassId(true), 0, rr);
if (isTeam)
oe->getTeams(art->getClassId(true), tt, false);
currentResult->prepareCalculations(*oe, false, {art->getClassId(true)}, rr, tt, inputNumber);
gdi_new->addString("", fontMediumPlus, "Debug Output");
if (!isTeam) {
oRunner &r = *pRunner(art);
currentResult->prepareCalculations(r, false);
int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown;
gdi.dropLine();
try {
st = currentResult->deduceStatus(r);
currentResult->debugDumpVariables(gdi, true);
gdi.addStringUT(1, L"ComputedStatus: " + oe->formatStatus(st, false)).setColor(colorGreen);
}
catch (meosException &ex) {
currentResult->debugDumpVariables(gdi, true);
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Status Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MDeduceRStatus))
currentResult->debugDumpVariables(gdi, false);
try {
rt = currentResult->deduceTime(r, r.getStartTime());
gdi.addStringUT(1, L"ComputedTime: " + formatTime(rt)).setColor(colorGreen);
}
catch (meosException &ex) {
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Time Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MDeduceRTime))
currentResult->debugDumpVariables(gdi, false);
try {
pt = currentResult->deducePoints(r);
gdi.addStringUT(1, "ComputedPoints: " + itos(pt)).setColor(colorGreen);
}
catch (meosException &ex) {
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Points Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MDeduceRPoints))
currentResult->debugDumpVariables(gdi, false);
try {
auto score = currentResult->score(r, st, rt, pt, false);
wstring str;
makeScore(str, score);
gdi.addStringUT(1, L"ComputedScore: " + str).setColor(colorGreen);
}
catch (meosException &ex) {
currentResult->debugDumpVariables(gdi, true);
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Score Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MRScore))
currentResult->debugDumpVariables(gdi, false);
}
else {
oTeam &t = *pTeam(art);
currentResult->prepareCalculations(t, false);
int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown;
gdi.dropLine();
try {
st = currentResult->deduceStatus(t);
currentResult->debugDumpVariables(gdi, true);
gdi.addStringUT(1, L"ComputedStatus: " + oe->formatStatus(st, false)).setColor(colorGreen);
}
catch (meosException &ex) {
currentResult->debugDumpVariables(gdi, true);
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Status Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MDeduceTStatus))
currentResult->debugDumpVariables(gdi, false);
try {
rt = currentResult->deduceTime(t);
gdi.addStringUT(1, L"ComputedTime: " + formatTime(rt)).setColor(colorGreen);
}
catch (meosException &ex) {
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Time Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MDeduceRTime))
currentResult->debugDumpVariables(gdi, false);
try {
pt = currentResult->deducePoints(t);
gdi.addStringUT(1, "ComputedPoints: " + itos(pt)).setColor(colorGreen);
}
catch (meosException &ex) {
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Points Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MDeduceTPoints))
currentResult->debugDumpVariables(gdi, false);
try {
auto score = currentResult->score(t, st, rt, pt);
wstring str;
makeScore(str, score);
gdi.addStringUT(1, L"ComputedScore:" + str).setColor(colorGreen);
}
catch (meosException &ex) {
currentResult->debugDumpVariables(gdi, true);
wstring err = lang.tl(ex.wwhat());
gdi.addString("", 0, L"Status Calculation Failed: X#" + err).setColor(colorRed);
}
if (currentResult->hasMethod(DynamicResult::MTScore))
currentResult->debugDumpVariables(gdi, false);
}
gdi.addButton("CloseWindow", "Stäng", methodCB);
gdi.refresh();
}
DynamicResult *MethodEditor::load(gdioutput &gdi, const string &tag, bool forceLoadCopy) {
DynamicResult *dr;
wstring srcFile;
DynamicResult &drIn = dynamic_cast<DynamicResult &>(*oe->getGeneralResult(tag, srcFile));
wasLoadedBuiltIn = drIn.isReadOnly();
dr = new DynamicResult(drIn);
if (forceLoadCopy || wasLoadedBuiltIn) {
dr->setTag(uniqueTag("result"));
dr->setName(lang.tl("Copy of ") + dr->getName(false));
setCurrentResult(dr, L"");
}
else
setCurrentResult(dr, srcFile);
if (forceLoadCopy || wasLoadedBuiltIn)
makeDirty(gdi, MakeDirty);
else
makeDirty(gdi, ClearDirty);
gdi.clearPage(false);
show(gdi);
if (dr) dr->compile(true);
return dr;
}