MeOS version 3.9.1399 Beta

This commit is contained in:
Erik Melin 2023-02-09 08:15:40 +01:00
parent 0adbb266e9
commit 032a39ab1e
177 changed files with 10182 additions and 4475 deletions

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -34,6 +34,9 @@
#include "Printer.h" #include "Printer.h"
#include "oListInfo.h" #include "oListInfo.h"
#include "meosexception.h" #include "meosexception.h"
#include "image.h"
extern Image image;
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
@ -139,10 +142,11 @@ public:
template<typename T, typename TI> template<typename T, typename TI>
void HTMLWriter::formatTL(ostream &fout, void HTMLWriter::formatTL(ostream &fout,
const map<pair<gdiFonts, string>, pair<string, string>> &styles, ImageWriter& imageWriter,
const T &tl, const map<pair<gdiFonts, string>, pair<string, string>> &styles,
double &yscale, double &xscale, const T &tl,
int &offsetY, int &offsetX) { double &yscale, double &xscale,
int &offsetY, int &offsetX) {
auto itt = tl.begin(); auto itt = tl.begin();
@ -158,6 +162,14 @@ void HTMLWriter::formatTL(ostream &fout,
string yp = itos(int(yscale*TI::y(ctr_it)) + offsetY); string yp = itos(int(yscale*TI::y(ctr_it)) + offsetY);
string xp = itos(int(xscale*TI::x(ctr_it)) + offsetX); string xp = itos(int(xscale*TI::x(ctr_it)) + offsetX);
if ((it.format & 0xFF) == textImage) {
int imgW = int((it.textRect.right - it.textRect.left) * xscale);
int imgH = int((it.textRect.bottom - it.textRect.top) * xscale);
imageWriter.write(fout, xp, yp, it.text, imgW, imgH);
++itt;
continue;
}
string estyle; string estyle;
if (it.format != 1 && it.format != boldSmall) { if (it.format != 1 && it.format != boldSmall) {
if (it.format & textRight) if (it.format & textRight)
@ -274,17 +286,31 @@ void HTMLWriter::writeHTML(gdioutput &gdi, const wstring &file,
if (fout.bad()) if (fout.bad())
throw std::exception("Bad output stream"); throw std::exception("Bad output stream");
writeHTML(gdi, fout, title, refreshTimeOut, scale); wchar_t drive[20];
wchar_t dir[MAX_PATH];
wchar_t name[MAX_PATH];
wchar_t ext[MAX_PATH];
_wsplitpath_s(file.c_str(), drive, dir, name, ext);
wstring path = wstring(drive) + dir;
writeHTML(gdi, fout, title, true, path, refreshTimeOut, scale);
} }
void HTMLWriter::writeHTML(gdioutput &gdi, ostream &fout, const wstring &title, int refreshTimeOut, double scale) { void HTMLWriter::writeHTML(gdioutput& gdi, ostream& fout,
const wstring& title,
bool includeImages,
const wstring& imageDirectoryDestination,
int refreshTimeOut, double scale) {
ImageWriter imgWriter(imageDirectoryDestination, includeImages);
if (scale <= 0) if (scale <= 0)
scale = 1.0; scale = 1.0;
fout << "<!DOCTYPE html>" << endl; fout << "<!DOCTYPE html>" << endl;
fout << "<html>\n<head>\n"; fout << "<html>\n<head>\n";
fout << "<meta charset=\"UTF-8\"/>\n"; fout << "<meta charset=\"UTF-8\"/>\n";
if (refreshTimeOut > 0) if (refreshTimeOut > 0)
fout << "<meta http-equiv=\"refresh\" content=\"" << refreshTimeOut << "\">\n"; fout << "<meta http-equiv=\"refresh\" content=\"" << refreshTimeOut << "\">\n";
@ -299,16 +325,16 @@ void HTMLWriter::writeHTML(gdioutput &gdi, ostream &fout, const wstring &title,
double yscale = 1.3 * scale; double yscale = 1.3 * scale;
double xscale = 1.2 * scale; double xscale = 1.2 * scale;
int offsetY = 0, offsetX = 0; int offsetY = 0, offsetX = 0;
HTMLWriter::formatTL<list<TextInfo>, InterpTextInfo>(fout, styles, gdi.getTL(), yscale, xscale, offsetY, offsetX); HTMLWriter::formatTL<list<TextInfo>, InterpTextInfo>(fout, imgWriter, styles, gdi.getTL(), yscale, xscale, offsetY, offsetX);
fout << "<p style=\"position:absolute;left:10px;top:" << int(yscale*(gdi.getPageY()-45)) + offsetY << "px\">"; fout << "<p style=\"position:absolute;left:10px;top:" << int(yscale * (gdi.getPageY() - 45)) + offsetY << "px\">";
char bf1[256]; char bf1[256];
char bf2[256]; char bf2[256];
GetTimeFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf2, 256); GetTimeFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf2, 256);
GetDateFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf1, 256); GetDateFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf1, 256);
//fout << "Skapad av <i>MeOS</i>: " << bf1 << " "<< bf2 << "\n"; //fout << "Skapad av <i>MeOS</i>: " << bf1 << " "<< bf2 << "\n";
fout << gdioutput::toUTF8(lang.tl("Skapad av ")) + "<a href=\"http://www.melin.nu/meos\" target=\"_blank\"><i>MeOS</i></a>: " << bf1 << " "<< bf2 << "\n"; fout << gdioutput::toUTF8(lang.tl("Skapad av ")) + "<a href=\"https://www.melin.nu/meos\" target=\"_blank\"><i>MeOS</i></a>: " << bf1 << " " << bf2 << "\n";
fout << "</p>\n"; fout << "</p>\n";
fout << "</body>\n"; fout << "</body>\n";
@ -341,18 +367,29 @@ void HTMLWriter::writeTableHTML(gdioutput &gdi,
if (fout.bad()) if (fout.bad())
return throw std::exception("Bad output stream"); return throw std::exception("Bad output stream");
writeTableHTML(gdi, fout, title, false, refreshTimeOut, scale); wchar_t drive[20];
wchar_t dir[MAX_PATH];
wchar_t name[MAX_PATH];
wchar_t ext[MAX_PATH];
_wsplitpath_s(file.c_str(), drive, dir, name, ext);
wstring path = wstring(drive) + dir;
writeTableHTML(gdi, fout, title, true, path, false, refreshTimeOut, scale);
} }
void HTMLWriter::writeTableHTML(gdioutput &gdi, void HTMLWriter::writeTableHTML(gdioutput& gdi,
ostream &fout, ostream& fout,
const wstring &title, const wstring& title,
bool includeImages,
const wstring& imageDirectoryDestination,
bool simpleFormat, bool simpleFormat,
int refreshTimeOut, int refreshTimeOut,
double scale) { double scale) {
if (scale <= 0) if (scale <= 0)
scale = 1.0; scale = 1.0;
ImageWriter imgWriter(imageDirectoryDestination, includeImages);
fout << "<!DOCTYPE html>" << endl; fout << "<!DOCTYPE html>" << endl;
fout << "<html>\n<head>\n"; fout << "<html>\n<head>\n";
fout << "<meta charset=\"UTF-8\"/>\n"; fout << "<meta charset=\"UTF-8\"/>\n";
@ -366,14 +403,14 @@ void HTMLWriter::writeTableHTML(gdioutput &gdi,
fout << "</head>\n"; fout << "</head>\n";
fout << "<body>\n"; fout << "<body>\n";
auto &TL = gdi.getTL(); auto& TL = gdi.getTL();
auto it = TL.begin(); auto it = TL.begin();
int MaxX = gdi.getPageX() - 100; int MaxX = gdi.getPageX() - 100;
map<int,int> tableCoordinates; map<int, int> tableCoordinates;
//Get x-coordinates //Get x-coordinates
while (it!=TL.end()) { while (it != TL.end()) {
tableCoordinates[it->xp]=0; tableCoordinates[it->xp] = 0;
++it; ++it;
} }
@ -384,47 +421,47 @@ void HTMLWriter::writeTableHTML(gdioutput &gdi,
++mit; ++mit;
} }
tableCoordinates[MaxX] = kr; tableCoordinates[MaxX] = kr;
vector<bool> sizeSet(kr + 1, false); vector<bool> sizeSet(kr + 1, false);
fout << "<table cellspacing=\"0\" border=\"0\">\n"; fout << "<table cellspacing=\"0\" border=\"0\">\n";
int linecounter=0; int linecounter = 0;
it=TL.begin(); it = TL.begin();
vector< pair<int, vector<const TextInfo *> > > rows; vector< pair<int, vector<const TextInfo*> > > rows;
rows.reserve(TL.size() / 3); rows.reserve(TL.size() / 3);
vector<int> ypRow; vector<int> ypRow;
int minHeight = 100000; int minHeight = 100000;
while (it!=TL.end()) { while (it != TL.end()) {
int y=it->yp; int y = it->yp;
vector<const TextInfo *> row; vector<const TextInfo*> row;
int subnormal = 0; int subnormal = 0;
int normal = 0; int normal = 0;
int header = 0; int header = 0;
int mainheader = 0; int mainheader = 0;
while (it!=TL.end() && it->yp==y) { while (it != TL.end() && it->yp == y) {
if (!gdioutput::skipTextRender(it->format)) { if (!gdioutput::skipTextRender(it->format)) {
row.push_back(&*it); row.push_back(&*it);
switch (it->getGdiFont()) { switch (it->getGdiFont()) {
case fontLarge: case fontLarge:
case boldLarge: case boldLarge:
case boldHuge: case boldHuge:
mainheader++; mainheader++;
break; break;
case boldText: case boldText:
case italicMediumPlus: case italicMediumPlus:
case fontMediumPlus: case fontMediumPlus:
header++; header++;
break; break;
case fontSmall: case fontSmall:
case italicSmall: case italicSmall:
subnormal++; subnormal++;
break; break;
default: default:
normal++; normal++;
} }
} }
++it; ++it;
@ -444,27 +481,27 @@ void HTMLWriter::writeTableHTML(gdioutput &gdi,
int last = ypRow.size(); int last = ypRow.size();
ypRow.push_back(y); ypRow.push_back(y);
if (last > 0) { if (last > 0) {
minHeight = min(minHeight, ypRow[last] - ypRow[last-1]); minHeight = min(minHeight, ypRow[last] - ypRow[last - 1]);
} }
} }
int numMin = 0; int numMin = 0;
for (size_t gCount = 1; gCount < rows.size(); gCount++) { for (size_t gCount = 1; gCount < rows.size(); gCount++) {
int h = ypRow[gCount] - ypRow[gCount-1]; int h = ypRow[gCount] - ypRow[gCount - 1];
if (h == minHeight) if (h == minHeight)
numMin++; numMin++;
} }
if (numMin == 0) if (numMin == 0)
numMin = 1; numMin = 1;
int hdrLimit = (rows.size() / numMin) <= 4 ? int(minHeight * 1.2) : int(minHeight * 1.5); int hdrLimit = (rows.size() / numMin) <= 4 ? int(minHeight * 1.2) : int(minHeight * 1.5);
for (size_t gCount = 1; gCount + 1 < rows.size(); gCount++) { for (size_t gCount = 1; gCount + 1 < rows.size(); gCount++) {
int type = rows[gCount].first; int type = rows[gCount].first;
int lastType = gCount > 0 ? rows[gCount-1].first : 0; int lastType = gCount > 0 ? rows[gCount - 1].first : 0;
int nextType = gCount + 1 < rows.size() ? rows[gCount + 1].first : 0; int nextType = gCount + 1 < rows.size() ? rows[gCount + 1].first : 0;
if (type == 0 && (lastType == 1 || lastType == 2) && (nextType == 1 || nextType == 2)) if (type == 0 && (lastType == 1 || lastType == 2) && (nextType == 1 || nextType == 2))
continue; // No reclassify continue; // No reclassify
int h = ypRow[gCount] - ypRow[gCount-1]; int h = ypRow[gCount] - ypRow[gCount - 1];
if (h > hdrLimit && rows[gCount].first == 0) if (h > hdrLimit && rows[gCount].first == 0)
rows[gCount].first = 2; rows[gCount].first = 2;
} }
@ -472,12 +509,12 @@ void HTMLWriter::writeTableHTML(gdioutput &gdi,
ypRow.clear(); ypRow.clear();
string lineclass; string lineclass;
for (size_t gCount = 0; gCount < rows.size(); gCount++) { for (size_t gCount = 0; gCount < rows.size(); gCount++) {
vector<const TextInfo *> &row = rows[gCount].second; vector<const TextInfo*>& row = rows[gCount].second;
int type = rows[gCount].first; int type = rows[gCount].first;
int lastType = gCount > 0 ? rows[gCount-1].first : 0; int lastType = gCount > 0 ? rows[gCount - 1].first : 0;
int nextType = gCount + 1 < rows.size() ? rows[gCount + 1].first : 0; int nextType = gCount + 1 < rows.size() ? rows[gCount + 1].first : 0;
vector<const TextInfo *>::iterator rit; vector<const TextInfo*>::iterator rit;
fout << "<tr>" << endl; fout << "<tr>" << endl;
if (simpleFormat) { if (simpleFormat) {
@ -498,48 +535,55 @@ void HTMLWriter::writeTableHTML(gdioutput &gdi,
lineclass = ""; lineclass = "";
} }
else else
lineclass = (linecounter&1) ? " class=\"e1\"" : " class=\"e0\""; lineclass = (linecounter & 1) ? " class=\"e1\"" : " class=\"e0\"";
linecounter++; linecounter++;
} }
for (size_t k=0;k<row.size();k++) { for (size_t k = 0; k < row.size(); k++) {
int thisCol=tableCoordinates[row[k]->xp]; int thisCol = tableCoordinates[row[k]->xp];
if (k == 0 && thisCol != 0) if (k == 0 && thisCol != 0)
fout << "<td" << lineclass << " colspan=\"" << thisCol << "\">&nbsp;</td>"; fout << "<td" << lineclass << " colspan=\"" << thisCol << "\">&nbsp;</td>";
int nextCol; int nextCol;
if (row.size() == k + 1) if (row.size() == k + 1)
nextCol = tableCoordinates.rbegin()->second; nextCol = tableCoordinates.rbegin()->second;
else else
nextCol = tableCoordinates[row[k + 1]->xp]; nextCol = tableCoordinates[row[k + 1]->xp];
int colspan = nextCol - thisCol; int colspan = nextCol - thisCol;
assert(colspan > 0); assert(colspan > 0);
string style; string style;
if (row[k]->format&textRight) if (row[k]->format & textRight)
style = " style=\"text-align:right\""; style = " style=\"text-align:right\"";
if (colspan == 1 && !sizeSet[thisCol]) { if (colspan == 1 && !sizeSet[thisCol]) {
fout << " <td" << lineclass << style << " width=\"" << int((k + 1 < row.size()) ? fout << " <td" << lineclass << style << " width=\"" << int((k + 1 < row.size()) ?
(row[k + 1]->xp - row[k]->xp) : (MaxX - row[k]->xp)) << "\">"; (row[k + 1]->xp - row[k]->xp) : (MaxX - row[k]->xp)) << "\">";
sizeSet[thisCol] = true; sizeSet[thisCol] = true;
} }
else if (colspan > 1) else if (colspan > 1)
fout << " <td" << lineclass << style << " colspan=\"" << colspan << "\">"; fout << " <td" << lineclass << style << " colspan=\"" << colspan << "\">";
else else
fout << " <td" << lineclass << style << ">"; fout << " <td" << lineclass << style << ">";
gdiFonts font = row[k]->getGdiFont(); if ((row[k]->format & 0xFF) == textImage) {
string starttag, endtag; int imgW = int((row[k]->textRect.right - row[k]->textRect.left) * scale);
getStyle(styles, font, gdioutput::narrow(row[k]->font), "", starttag, endtag); int imgH = int((row[k]->textRect.bottom - row[k]->textRect.top) * scale);
imgWriter.write(fout, "", "", row[k]->text, imgW, imgH);
fout << starttag << gdioutput::toUTF8(html_table_code(row[k]->text)) << endtag << "</td>" << endl; fout << "</td>" << endl;
}
else {
gdiFonts font = row[k]->getGdiFont();
string starttag, endtag;
getStyle(styles, font, gdioutput::narrow(row[k]->font), "", starttag, endtag);
fout << starttag << gdioutput::toUTF8(html_table_code(row[k]->text)) << endtag << "</td>" << endl;
}
} }
fout << "</tr>\n"; fout << "</tr>\n";
@ -555,7 +599,7 @@ fout << starttag << gdioutput::toUTF8(html_table_code(row[k]->text)) << endtag <
GetTimeFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf2, 256); GetTimeFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf2, 256);
GetDateFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf1, 256); GetDateFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf1, 256);
wstring meos = getMeosCompectVersion(); wstring meos = getMeosCompectVersion();
fout << gdioutput::toUTF8(lang.tl("Skapad av ")) + "<a href=\"http://www.melin.nu/meos\" target=\"_blank\"><i>MeOS " fout << gdioutput::toUTF8(lang.tl("Skapad av ")) + "<a href=\"https://www.melin.nu/meos\" target=\"_blank\"><i>MeOS "
<< gdioutput::toUTF8(meos) << "</i></a>: " << bf1 << " " << bf2 << "\n"; << gdioutput::toUTF8(meos) << "</i></a>: " << bf1 << " " << bf2 << "\n";
fout << "</p><br>\n"; fout << "</p><br>\n";
} }
@ -781,8 +825,11 @@ void HTMLWriter::generate(gdioutput &gdi,
double scale) const { double scale) const {
int w, h; int w, h;
gdi.getTargetDimension(w, h); gdi.getTargetDimension(w, h);
ImageWriter imgWriter(L"", false);
string meos = "<a href=\"http://www.melin.nu/meos\" target=\"_blank\"><i>MeOS</i></a>: " + gdioutput::toUTF8(getMeosCompectVersion()) + "</a>"; string meos = "<a href=\"https://www.melin.nu/meos\" target=\"_blank\"><i>MeOS</i></a>: " + gdioutput::toUTF8(getMeosCompectVersion()) + "</a>";
int margin = (w * marginPercent) / 100; int margin = (w * marginPercent) / 100;
int height = nRows * gdi.getLineHeight(); int height = nRows * gdi.getLineHeight();
@ -886,7 +933,7 @@ void HTMLWriter::generate(gdioutput &gdi,
double xscale = 1.2 * scale; double xscale = 1.2 * scale;
int offsetY = 0, offsetX = 0; int offsetY = 0, offsetX = 0;
formatTL<vector<PrintTextInfo>, InterpPrintTextInfo>(sout, styles, p.text, yscale, xscale, offsetY, offsetX); formatTL<vector<PrintTextInfo>, InterpPrintTextInfo>(sout, imgWriter, styles, p.text, yscale, xscale, offsetY, offsetX);
output = innerpage; output = innerpage;
replaceAll(output, "@P", itos(ipCounter++)); replaceAll(output, "@P", itos(ipCounter++));
@ -921,17 +968,27 @@ void HTMLWriter::write(gdioutput &gdi, const wstring &file, const wstring &title
checkWriteAccess(file); checkWriteAccess(file);
ofstream fout(file.c_str()); ofstream fout(file.c_str());
write(gdi, fout, title, contentsDescription, respectPageBreak, typeTag, refresh, wchar_t drive[20];
wchar_t dir[MAX_PATH];
wchar_t name[MAX_PATH];
wchar_t ext[MAX_PATH];
_wsplitpath_s(file.c_str(), drive, dir, name, ext);
wstring path = wstring(drive) + dir;
write(gdi, fout, title, true, path, contentsDescription, respectPageBreak, typeTag, refresh,
rows, cols, time_ms, margin, scale); rows, cols, time_ms, margin, scale);
} }
void HTMLWriter::write(gdioutput &gdi, ostream &fout, const wstring &title, const wstring &contentsDescription, void HTMLWriter::write(gdioutput &gdi, ostream &fout, const wstring &title,
bool includeImages,
const wstring& imageDirectoryDestination,
const wstring &contentsDescription,
bool respectPageBreak, const string &typeTag, int refresh, bool respectPageBreak, const string &typeTag, int refresh,
int rows, int cols, int time_ms, int margin, double scale) { int rows, int cols, int time_ms, int margin, double scale) {
if (typeTag == "table") if (typeTag == "table")
writeTableHTML(gdi, fout, title, false, refresh, scale); writeTableHTML(gdi, fout, title, includeImages, imageDirectoryDestination, false, refresh, scale);
else if (typeTag == "free") { else if (typeTag == "free") {
writeHTML(gdi, fout, title, refresh, scale); writeHTML(gdi, fout, title, includeImages, imageDirectoryDestination, refresh, scale);
} }
else { else {
/* auto res = tCache.find(typeTag); /* auto res = tCache.find(typeTag);
@ -972,6 +1029,44 @@ void HTMLWriter::write(gdioutput &gdi, const wstring &file, const wstring &title
param.timePerPage, param.margin, param.htmlScale); param.timePerPage, param.margin, param.htmlScale);
} }
void HTMLWriter::write(gdioutput& gdi, ostream& fout, const wstring& title, int refresh, oListParam& param, const oEvent& oe) {
write(gdi, fout, title, true, L"", param.getContentsDescriptor(oe), param.pageBreak, param.htmlTypeTag,
refresh != 0 ? refresh : param.timePerPage / 1000, param.htmlRows, param.nColumns,
param.timePerPage, param.margin, param.htmlScale);
}
void HTMLWriter::getPage(const oEvent &oe, string &out) const { void HTMLWriter::getPage(const oEvent &oe, string &out) const {
out = page; out = page;
} }
void HTMLWriter::ImageWriter::write(ostream& fout, const string& xp, const string& yp, const wstring& img, int width, int height) {
if (!writeImages) {
if (xp.empty())
fout << "&nbsp;";
}
else {
if (img.empty() || img[0] != 'L')
throw meosException("Unsupported image");
uint64_t imgId = _wcstoui64(img.c_str() + 1, nullptr, 10);
if (!savedFiles.count(imgId)) {
if (!destination.empty()) {
auto& data = image.getRawData(imgId);
wstring d = destination + img + L".png";
ofstream out(d, ofstream::out | ofstream::binary);
out.write((const char *)data.data(), data.size());
savedFiles[imgId] = "L" + itos(imgId) + ".png";
}
else {
savedFiles[imgId] = "/meos?image=ID" + itos(imgId) + ".png";
}
}
string style;
if (xp.size() > 0)
style = " style=\"position:absolute;left:" + xp + "px;top:" + yp + "px\"";
fout << "<img src=\"" << savedFiles[imgId] << "\" width=\"" <<
width << "\" height=\"" << height << "\"" << style << ">";
}
}

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -39,6 +39,29 @@ class HTMLWriter {
static string localize(const string &in); static string localize(const string &in);
class ImageWriter {
wstring destination;
const bool writeImages;
const wstring imageDirectoryDestination;
map<uint64_t, string> savedFiles;
public:
ImageWriter(const wstring& dst, bool writeImages) : destination(dst), writeImages(writeImages) {}
void write(ostream &fout, const string &xp, const string &yp, const wstring &img, int width, int height);
};
template<typename T, typename TI>
static void formatTL(ostream& fout,
ImageWriter& imageWriter,
const map< pair<gdiFonts, string>, pair<string, string> >& styles,
const T& tl,
double& yscale,
double& xscale,
int& offsetY,
int& offsetX);
public: public:
static void reset() { static void reset() {
@ -76,10 +99,15 @@ public:
void getPage(const oEvent &oe, string &out) const; void getPage(const oEvent &oe, string &out) const;
static void writeHTML(gdioutput &gdi, ostream &dout, const wstring &title, int refreshTimeOut, double scale); static void writeHTML(gdioutput &gdi, ostream &dout, const wstring &title,
bool includeImages,
const wstring& imageDirectoryDestination,
int refreshTimeOut, double scale);
static void writeTableHTML(gdioutput &gdi, ostream &fout, static void writeTableHTML(gdioutput &gdi, ostream &fout,
const wstring &title, const wstring &title,
bool includeImages,
const wstring &imageDirectoryDestination,
bool simpleFormat, bool simpleFormat,
int refreshTimeOut, int refreshTimeOut,
double scale); double scale);
@ -92,23 +120,17 @@ public:
static void writeHTML(gdioutput &gdi, const wstring &file, static void writeHTML(gdioutput &gdi, const wstring &file,
const wstring &title, int refreshTimeOut, double scale); const wstring &title, int refreshTimeOut, double scale);
static void write(gdioutput &gdi, const wstring &file, const wstring &title, const wstring &contentsDescription, static void write(gdioutput& gdi, const wstring& file, const wstring& title, int refresh, oListParam& param, const oEvent& oe);
bool respectPageBreak, const string &typeTag, int refresh, static void write(gdioutput& gdi, ostream& fout, const wstring& title, int refresh, oListParam& param, const oEvent& oe);
int rows, int cols, int time_ms, int margin, double scale);
static void write(gdioutput &gdi, ostream &fout, const wstring &title, const wstring &contentsDescription, static void write(gdioutput& gdi, const wstring& file, const wstring& title, const wstring& contentsDescription,
bool respectPageBreak, const string &typeTag, int refresh, bool respectPageBreak, const string& typeTag, int refresh,
int rows, int cols, int time_ms, int margin, double scale); int rows, int cols, int time_ms, int margin, double scale);
static void write(gdioutput &gdi, const wstring &file, const wstring &title, int refresh, oListParam &param, const oEvent &oe);
template<typename T, typename TI>
static void formatTL(ostream &fout,
const map< pair<gdiFonts, string>, pair<string, string> > &styles,
const T &tl,
double &yscale,
double &xscale,
int &offsetY,
int &offsetX);
static void write(gdioutput& gdi, ostream& fout, const wstring& title,
bool includeImages,
const wstring& imageDirectoryDestination,
const wstring& contentsDescription,
bool respectPageBreak, const string& typeTag, int refresh,
int rows, int cols, int time_ms, int margin, double scale);
}; };

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -99,6 +99,11 @@ string C_INT64(string name)
return " "+name+" BIGINT NOT NULL DEFAULT 0, "; return " "+name+" BIGINT NOT NULL DEFAULT 0, ";
} }
string C_UINT64(string name)
{
return " " + name + " BIGINT UNSIGNED NOT NULL DEFAULT 0, ";
}
string C_STRING(string name, int len=64) string C_STRING(string name, int len=64)
{ {
char bf[16]; char bf[16];
@ -451,8 +456,21 @@ void MeosSQL::upgradeDB(const string &db, oDataContainer const * dc) {
sql = sql.substr(0, sql.length() - 2); sql = sql.substr(0, sql.length() - 2);
query.execute(sql); query.execute(sql);
} }
if (!eCol.count("BDate")) {
string sql = "ALTER TABLE " + db + " ";
sql += "ADD COLUMN " + C_INT("BDate");
sql = sql.substr(0, sql.length() - 2);
query.execute(sql);
}
}
else if (db == "oPunch") {
if (!eCol.count("Unit")) {
string sql = "ALTER TABLE " + db + " ";
sql += "ADD COLUMN " + C_INT("Unit");
sql = sql.substr(0, sql.length() - 2);
query.execute(sql);
}
} }
if (dc) { if (dc) {
// Ugrade table // Ugrade table
string sqlAdd = dc->generateSQLDefinition(eCol); string sqlAdd = dc->generateSQLDefinition(eCol);
@ -478,7 +496,8 @@ bool MeosSQL::openDB(oEvent *oe)
return 0; return 0;
} }
monitorId = 0; monitorId = 0;
string dbname(oe->currentNameId.begin(), oe->currentNameId.end());//WCS string dbname(oe->currentNameId.begin(), oe->currentNameId.end());
bool tookLock = false;
try { try {
auto query = con->query(); auto query = con->query();
@ -489,11 +508,29 @@ bool MeosSQL::openDB(oEvent *oe)
auto row = res.at(0); auto row = res.at(0);
int version = row["Version"]; int version = row["Version"];
if (version < oEvent::dbVersion) { if (version < oEvent::dbVersion) {
if (version <= 88) {
query.exec("LOCK TABLE MeOSMain.oEvent WRITE");
tookLock = true;
query.reset();
query << "SELECT Version FROM oEvent WHERE NameId=" << quote << dbname;
auto resV = query.store();
if (res && res.num_rows() >= 1) {
version = res.at(0)["Version"];
}
}
query.reset(); query.reset();
query << "UPDATE oEvent SET Version=" << oEvent::dbVersion << " WHERE Id=" << int(row["Id"]); query << "UPDATE oEvent SET Version=" << oEvent::dbVersion << " WHERE Id=" << int(row["Id"]);
query.execute(); query.execute();
if (version <= 88) {
upgradeTimeFormat(dbname);
}
if (tookLock) {
con->query().exec("UNLOCK TABLES");
}
} }
else if (version > oEvent::dbVersion) { else if (version > oEvent::dbVersion) {
alert("A newer version av MeOS is required."); alert("A newer version av MeOS is required.");
@ -524,6 +561,10 @@ bool MeosSQL::openDB(oEvent *oe)
} }
} }
catch (const Exception& er) { catch (const Exception& er) {
if (tookLock && con) {
con->query().exec("UNLOCK TABLES");
}
setDefaultDB(); setDefaultDB();
alert(string(er.what()) + " MySQL Error. Select DB."); alert(string(er.what()) + " MySQL Error. Select DB.");
return 0; return 0;
@ -588,6 +629,7 @@ bool MeosSQL::openDB(oEvent *oe)
<< C_INT("CardNo") << C_INT("CardNo")
<< C_UINT("ReadId") << C_UINT("ReadId")
<< C_UINT("Voltage") << C_UINT("Voltage")
<< C_INT("BDate")
<< C_STRING("Punches", 16*190) << C_END(); << C_STRING("Punches", 16*190) << C_END();
query.execute(); query.execute();
@ -657,9 +699,12 @@ bool MeosSQL::openDB(oEvent *oe)
query << C_START("oPunch") query << C_START("oPunch")
<< C_INT("CardNo") << C_INT("CardNo")
<< C_INT("Time") << C_INT("Time")
<< C_INT("Type") << C_END(); << C_INT("Type")
<< C_INT("Unit") << C_END();
query.execute(); query.execute();
upgradeDB("oPunch", nullptr);
query.reset(); query.reset();
query << C_START("oMonitor") query << C_START("oMonitor")
<< C_STRING("Client") << C_STRING("Client")
@ -691,6 +736,13 @@ bool MeosSQL::openDB(oEvent *oe)
// Create runner/club DB // Create runner/club DB
createRunnerDB(oe, query); createRunnerDB(oe, query);
query.reset();
query << C_START_noid("oImage")
<< C_UINT64("Id")
<< C_TEXT("Filename")
<< " Image LONGBLOB)" << engine();
query.execute();
} }
catch (const Exception& er){ catch (const Exception& er){
alert(string(er.what()) + " MySQL Error."); alert(string(er.what()) + " MySQL Error.");
@ -979,9 +1031,9 @@ OpFailStatus MeosSQL::uploadRunnerDB(oEvent *oe)
auto query = con->query(); auto query = con->query();
query << "INSERT INTO dbRunner SET " << query << "INSERT INTO dbRunner SET " <<
"Name=" << quote << rdb[k].name << "Name=" << quote << rdb[k].name <<
", ExtId=" << rdb[k].extId << ", Club=" << rdb[k].clubNo << ", ExtId=" << rdb[k].getExtId() << ", Club=" << rdb[k].clubNo <<
", CardNo=" << rdb[k].cardNo << ", Sex=" << quote << rdb[k].getSex() << ", CardNo=" << rdb[k].cardNo << ", Sex=" << quote << rdb[k].getSex() <<
", Nation=" << quote << rdb[k].getNationality() << ", BirthYear=" << rdb[k].birthYear; ", Nation=" << quote << rdb[k].getNationality() << ", BirthYear=" << rdb[k].getBirthDateInt();
try { try {
query.execute(); query.execute();
@ -1031,7 +1083,7 @@ bool MeosSQL::storeData(oDataInterface odi, const RowWrapper &row, unsigned long
} }
} }
else { else {
__int64 val = row[(const char*)it_int->name].ulonglong(); __int64 val = row[(const char*)it_int->name].longlong();
__int64 oldVal = *(it_int->data64); __int64 oldVal = *(it_int->data64);
if (val != oldVal) { if (val != oldVal) {
memcpy(it_int->data64, &val, 8); memcpy(it_int->data64, &val, 8);
@ -1476,7 +1528,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) {
RunnerDBEntry &dbn = db->dbe(); RunnerDBEntry &dbn = db->dbe();
if (sex.length()==1) if (sex.length()==1)
dbn.sex = sex[0]; dbn.sex = sex[0];
dbn.birthYear = short(atoi(birth.c_str())); dbn.setBirthDate(atoi(birth.c_str()));
if (nat.length()==3) { if (nat.length()==3) {
dbn.national[0] = nat[0]; dbn.national[0] = nat[0];
@ -1546,6 +1598,7 @@ void MeosSQL::storeCard(const RowWrapper &row, oCard &c)
c.cardNo = row["CardNo"]; c.cardNo = row["CardNo"];
c.readId = row["ReadId"]; c.readId = row["ReadId"];
c.miliVolt = row["Voltage"]; c.miliVolt = row["Voltage"];
c.batteryDate = row["BDate"];
c.importPunches(string(row["Punches"])); c.importPunches(string(row["Punches"]));
c.sqlUpdated = row["Modified"]; c.sqlUpdated = row["Modified"];
@ -1565,10 +1618,10 @@ void MeosSQL::storePunch(const RowWrapper &row, oFreePunch &p, bool rehash)
} }
else { else {
p.CardNo = row["CardNo"]; p.CardNo = row["CardNo"];
p.Time = row["Time"]; p.punchTime = row["Time"];
p.Type = row["Type"]; p.type = row["Type"];
} }
p.punchUnit = row["Unit"];
p.sqlUpdated = row["Modified"]; p.sqlUpdated = row["Modified"];
p.counter = row["Counter"]; p.counter = row["Counter"];
p.Removed = row["Removed"]; p.Removed = row["Removed"];
@ -2178,6 +2231,7 @@ OpFailStatus MeosSQL::syncUpdate(oCard *c, bool forceWriteAll)
auto queryset = con->query(); auto queryset = con->query();
queryset << " CardNo=" << c->cardNo queryset << " CardNo=" << c->cardNo
<< ", ReadId=" << c->readId << ", Voltage=" << max(0, c->miliVolt) << ", ReadId=" << c->readId << ", Voltage=" << max(0, c->miliVolt)
<< ", BDate=" << c->batteryDate
<< ", Punches=" << quote << c->getPunchString(); << ", Punches=" << quote << c->getPunchString();
return syncUpdate(queryset, "oCard", c); return syncUpdate(queryset, "oCard", c);
@ -2287,6 +2341,77 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t)
return syncRead(forceRead, t, true); return syncRead(forceRead, t, true);
} }
void MeosSQL::upgradeTimeFormat(const string & dbname) {
bool ok = false;
try {
con->select_db(dbname);
ok = true;
}
catch (const Exception &) {
}
if (ok) {
auto query = con->query();
query.exec("LOCK TABLES oEvent WRITE, oClass WRITE, oControl WRITE, "
"oCourse WRITE, oPunch WRITE, oRunner WRITE, oTeam WRITE");
}
else
return;
auto upgradeCol = [this](const string &db, const string &col) {
auto query = con->query();
string v = "UPDATE " + db + " SET " + col + "=" + col + "*10 WHERE " + col + "<>-1";
try {
query.execute(v);
}
catch (const Exception &) {
return false;
}
return true;
};
auto alter = [this](const string &db, const string &col) {
auto query = con->query();
string v = "ALTER TABLE " + db + " MODIFY COLUMN " + col + " INT NOT NULL DEFAULT 0";
try {
query.execute(v);
}
catch (const Exception &) {
return false;
}
return true;
};
upgradeCol("oEvent", "ZeroTime");
upgradeCol("oEvent", "MaxTime");
upgradeCol("oEvent", "DiffTime");
upgradeCol("oClass", "FirstStart");
upgradeCol("oClass", "StartInterval");
upgradeCol("oClass", "MaxTime");
upgradeCol("oControl", "TimeAdjust");
upgradeCol("oControl", "MinTime");
upgradeCol("oCourse", "RTimeLimit");
upgradeCol("oPunch", "Time");
upgradeCol("oRunner", "StartTime");
upgradeCol("oRunner", "FinishTime");
upgradeCol("oRunner", "InputTime");
alter("oRunner", "TimeAdjust");
upgradeCol("oRunner", "TimeAdjust");
upgradeCol("oRunner", "EntryTime");
upgradeCol("oTeam", "StartTime");
upgradeCol("oTeam", "FinishTime");
upgradeCol("oTeam", "InputTime");
alter("oTeam", "TimeAdjust");
upgradeCol("oTeam", "TimeAdjust");
upgradeCol("oTeam", "EntryTime");
}
OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t, bool readRecursive) OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t, bool readRecursive)
{ {
errorMessage.clear(); errorMessage.clear();
@ -2536,7 +2661,7 @@ OpFailStatus MeosSQL::syncReadControls(oEvent *oe, const set<int> &controls) {
int counter = row["Counter"]; int counter = row["Counter"];
string modified = row["Modified"]; string modified = row["Modified"];
pControl pc = oe->getControl(id, false); pControl pc = oe->getControl(id, false, false);
if (!pc) { if (!pc) {
oControl oc(oe, id); oControl oc(oe, id);
success = min(success, syncRead(true, &oc)); success = min(success, syncRead(true, &oc));
@ -2550,7 +2675,7 @@ OpFailStatus MeosSQL::syncReadControls(oEvent *oe, const set<int> &controls) {
// processedCourses should now be empty, unless there are local controls not yet added. // processedCourses should now be empty, unless there are local controls not yet added.
for(set<int>::iterator it = processedControls.begin(); it != processedControls.end(); ++it) { for(set<int>::iterator it = processedControls.begin(); it != processedControls.end(); ++it) {
pControl pc = oe->getControl(*it, false); pControl pc = oe->getControl(*it, false, false);
if (pc) { if (pc) {
success = min(success, syncUpdate(pc, true)); success = min(success, syncUpdate(pc, true));
} }
@ -2647,7 +2772,7 @@ OpFailStatus MeosSQL::syncUpdate(oControl *c, bool forceWriteAll) {
auto queryset = con->query(); auto queryset = con->query();
queryset << " Name=" << quote << toString(c->Name) << ", " queryset << " Name=" << quote << toString(c->Name) << ", "
<< " Numbers=" << quote << toString(c->codeNumbers()) << "," << " Numbers=" << quote << toString(c->codeNumbers()) << ","
<< " Status=" << c->Status << " Status=" << int(c->Status)
<< c->getDI().generateSQLSet(forceWriteAll); << c->getDI().generateSQLSet(forceWriteAll);
return syncUpdate(queryset, "oControl", c); return syncUpdate(queryset, "oControl", c);
@ -2826,8 +2951,9 @@ OpFailStatus MeosSQL::syncUpdate(oFreePunch *c, bool forceWriteAll)
} }
auto queryset = con->query(); auto queryset = con->query();
queryset << " CardNo=" << c->CardNo << ", " queryset << " CardNo=" << c->CardNo << ", "
<< " Type=" << c->Type << "," << " Type=" << c->type << ","
<< " Time=" << c->Time; << " Time=" << c->punchTime << ","
<< " Unit=" << c->punchUnit;
return syncUpdate(queryset, "oPunch", c); return syncUpdate(queryset, "oPunch", c);
} }
@ -2974,38 +3100,55 @@ OpFailStatus MeosSQL::syncUpdate(QueryWrapper &updateqry,
if (ob->isRemoved()) if (ob->isRemoved())
return opStatusOK; return opStatusOK;
bool setId = false; bool setId = false;
bool update = false;
if (ob->Id > 0) { if (ob->Id > 0) {
query << "SELECT Id FROM " << oTable << " WHERE Id=" << ob->Id; query << "SELECT Removed FROM " << oTable << " WHERE Id=" << ob->Id;
auto res=query.store(); auto res=query.store();
if (res.empty()) if (res.empty())
setId = true; setId = true;
else if (ob->isImplicitlyCreated()) { else if (ob->isImplicitlyCreated()) {
return opStatusWarning;//XXX Should we read this object? return opStatusWarning;//XXX Should we read this object?
} }
else {
int removed = res.at(0).at(0);
if (removed) {
update = true;
}
}
} }
else { else {
assert(!ob->isImplicitlyCreated()); assert(!ob->isImplicitlyCreated());
} }
query.reset(); query.reset();
query << "INSERT INTO " << oTable << " SET " << updateqry.str(); if (update) {
query << "UPDATE " << oTable << " SET Removed = 0, " << updateqry.str();
}
else {
query << "INSERT INTO " << oTable << " SET " << updateqry.str();
if (setId) if (setId)
query << ", Id=" << ob->Id; query << ", Id=" << ob->Id;
}
if (writeTime) { if (writeTime) {
query << ", Modified='" << ob->getTimeStampN() << "'"; query << ", Modified='" << ob->getTimeStampN() << "'";
} }
if (update) {
query << " WHERE Id=" << ob->Id;
}
ResNSel res=query.execute(); ResNSel res=query.execute();
if (res) { if (res) {
if (ob->Id > 0 && ob->Id!=(int)res.insert_id) { if (!update) {
ob->correctionNeeded = true; if (ob->Id > 0 && ob->Id != (int)res.insert_id) {
} ob->correctionNeeded = true;
}
if (ob->Id != res.insert_id) if (ob->Id != res.insert_id)
ob->changeId((int)res.insert_id); ob->changeId((int)res.insert_id);
}
updateCounter(oTable, ob->Id, 0); updateCounter(oTable, ob->Id, 0);
ob->oe->updateFreeId(ob); ob->oe->updateFreeId(ob);
@ -3059,8 +3202,12 @@ OpFailStatus MeosSQL::syncUpdate(QueryWrapper &updateqry,
bool MeosSQL::checkOldVersion(oEvent *oe, RowWrapper &row) { bool MeosSQL::checkOldVersion(oEvent *oe, RowWrapper &row) {
int dbv=int(row["BuildVersion"]); int dbv=int(row["BuildVersion"]);
if ( dbv<buildVersion ) if (dbv < buildVersion) {
oe->updateChanged(); string bv = "UPDATE oEvent SET BuildVersion=if (BuildVersion<" +
itos(buildVersion) + "," + itos(buildVersion) + ",BuildVersion) WHERE Id = " + itos(oe->Id);
con->query().exec(bv);
}
else if (dbv>buildVersion) else if (dbv>buildVersion)
return true; return true;
@ -3552,7 +3699,7 @@ bool MeosSQL::syncListControl(oEvent *oe) {
if (int(row["Removed"])) { if (int(row["Removed"])) {
st = opStatusOK; st = opStatusOK;
oControl *c = oe->getControl(Id, false); oControl *c = oe->getControl(Id, false, false);
if (c && !c->Removed) { if (c && !c->Removed) {
c->Removed = true; c->Removed = true;
@ -3562,7 +3709,7 @@ bool MeosSQL::syncListControl(oEvent *oe) {
} }
} }
else { else {
oControl *c = oe->getControl(Id, false); oControl *c = oe->getControl(Id, false, false);
if (c) { if (c) {
if (isOld(counter, modified, c)) if (isOld(counter, modified, c))
st = syncRead(false, c); st = syncRead(false, c);
@ -3848,11 +3995,12 @@ bool MeosSQL::dropDatabase(oEvent *oe)
return 0; return 0;
} }
string error;
try { try {
con->drop_db(CmpDataBase); con->drop_db(CmpDataBase);
} }
catch (const Exception& ) { catch (const Exception& ex) {
//Don't care if we fail. error = ex.what();
} }
try { try {
@ -3860,7 +4008,10 @@ bool MeosSQL::dropDatabase(oEvent *oe)
query << "DELETE FROM oEvent WHERE NameId=" << quote << CmpDataBase; query << "DELETE FROM oEvent WHERE NameId=" << quote << CmpDataBase;
query.execute(); query.execute();
} }
catch (const Exception& ) { catch (const Exception& ex) {
if (!error.empty())
error += ", ";
error += ex.what();
//Don't care if we fail. //Don't care if we fail.
} }
@ -3874,6 +4025,9 @@ bool MeosSQL::dropDatabase(oEvent *oe)
catch (const Exception&) { catch (const Exception&) {
} }
if (!error.empty())
throw meosException(error);
return true; return true;
} }
@ -3930,7 +4084,9 @@ int getTypeId(const oBase &ob)
} }
static int skipped = 0, notskipped = 0, readent = 0; static int skipped = 0, notskipped = 0, readent = 0;
void MeosSQL::synchronized(const oBase &entity) { void MeosSQL::synchronized(oBase &entity) {
entity.Modified.setStamp(entity.sqlUpdated);
int id = getTypeId(entity); int id = getTypeId(entity);
readTimes[make_pair(id, entity.getId())] = GetTickCount(); readTimes[make_pair(id, entity.getId())] = GetTickCount();
readent++; readent++;
@ -4460,3 +4616,96 @@ OpFailStatus MeosSQL::synchronizeUpdate(oBase *obj) {
return OpFailStatus::opStatusFail; return OpFailStatus::opStatusFail;
} }
OpFailStatus MeosSQL::enumerateImages(vector<pair<wstring, uint64_t>>& images) {
try {
auto query = con->query();
images.clear();
auto res = query.store("SELECT Id, Filename FROM oImage");
if (res) {
for (int i = 0; i < res.num_rows(); i++) {
auto row = res.at(i);
wstring fileName = fromUTF((string)row["Filename"]);
uint64_t id = row["Id"].ulonglong();
images.emplace_back(fileName, id);
}
}
}
catch (const Exception& ) {
return OpFailStatus::opStatusFail;
}
return OpFailStatus::opStatusOK;
}
OpFailStatus MeosSQL::getImage(uint64_t id, wstring& fileName, vector<uint8_t>& data) {
try {
auto query = con->query();
auto res = query.store("SELECT * FROM oImage WHERE id=" + itos(id));
if (res && res.num_rows() > 0) {
auto row = res.at(0);
fileName = fromUTF((string)row["Filename"]);
row["Image"].storeBlob(data);
return OpFailStatus::opStatusOK;
}
}
catch (const Exception&) {
}
return OpFailStatus::opStatusFail;
}
string hexEncode(const vector<uint8_t>& data) {
string out;
out.reserve(4 + data.size() * 2);
out.append("X'");
char table[17] = "0123456789ABCDEF";
char block[33];
block[32] = 0;
for (int j = 0; j < data.size();) {
if (j + 16 < data.size()) {
for (int i = 0; i < 32; ) {
uint8_t b = data[j];
int bLow = b & 0xF;
int bHigh = (b >> 4) & 0xF;
block[i++] = table[bHigh];
block[i++] = table[bLow];
j++;
}
}
else {
int i = 0;
while (j < data.size()) {
uint8_t b = data[j];
int bLow = b & 0xF;
int bHigh = (b >> 4) & 0xF;
block[i++] = table[bHigh];
block[i++] = table[bLow];
j++;
}
block[i] = 0;
}
out.append(block);
}
out.append("'");
return out;
}
OpFailStatus MeosSQL::storeImage(uint64_t id, const wstring& fileName, const vector<uint8_t>& data) {
try {
auto query = con->query();
auto res = query.store("SELECT Id FROM oImage WHERE Id=" + itos(id));
if (res.empty()) {
query << ("INSERT INTO oImage SET Id=" + itos(id) + ", Filename=")
<< quote << toString(fileName) << ", Image=" << hexEncode(data);
query.execute();
}
}
catch (Exception &) {
return OpFailStatus::opStatusFail;
}
return OpFailStatus::opStatusOK;
}

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -131,7 +131,7 @@ protected:
bool checkOldVersion(oEvent *oe, RowWrapper &row); bool checkOldVersion(oEvent *oe, RowWrapper &row);
map<pair<int, int>, DWORD> readTimes; map<pair<int, int>, DWORD> readTimes;
void synchronized(const oBase &entity); void synchronized(oBase &entity);
bool skipSynchronize(const oBase &entity) const; bool skipSynchronize(const oBase &entity) const;
ResNSel updateCounter(const char *oTable, int id, QueryWrapper *updateqry); ResNSel updateCounter(const char *oTable, int id, QueryWrapper *updateqry);
@ -186,9 +186,14 @@ protected:
OpFailStatus syncUpdate(oTeam *t, bool forceWriteAll); OpFailStatus syncUpdate(oTeam *t, bool forceWriteAll);
OpFailStatus syncRead(bool forceRead, oTeam *t); OpFailStatus syncRead(bool forceRead, oTeam *t);
void upgradeTimeFormat(const string &dbname);
public: public:
OpFailStatus enumerateImages(vector<pair<wstring, uint64_t>>& images);
OpFailStatus getImage(uint64_t id, wstring &fileName, vector<uint8_t> &data);
OpFailStatus storeImage(uint64_t id, const wstring& fileName, const vector<uint8_t>& data);
bool synchronizeList(oEvent *oe, oListId lid); bool synchronizeList(oEvent *oe, oListId lid);
OpFailStatus synchronizeUpdate(oBase *obj); OpFailStatus synchronizeUpdate(oBase *obj);

View File

@ -4,7 +4,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -237,12 +237,12 @@ wstring RunnerWDBEntry::getFamilyName() const
__int64 RunnerWDBEntry::getExtId() const __int64 RunnerWDBEntry::getExtId() const
{ {
return dbe().extId; return dbe().getExtId();
} }
void RunnerWDBEntry::setExtId(__int64 id) void RunnerWDBEntry::setExtId(__int64 id)
{ {
dbe().extId = id; dbe().setExtId(id);
} }
void RunnerDBEntryV2::init(const RunnerDBEntryV1 &dbe) void RunnerDBEntryV2::init(const RunnerDBEntryV1 &dbe)
@ -303,7 +303,7 @@ RunnerWDBEntry *RunnerDB::addRunner(const wchar_t *name,
en.cardNo = card; en.cardNo = card;
en.clubNo = club; en.clubNo = club;
e.setName(name); e.setName(name);
en.extId = extId; en.setExtId(extId);
if (!check(en) ) { if (!check(en) ) {
rdb.pop_back(); rdb.pop_back();
@ -336,7 +336,7 @@ RunnerWDBEntry *RunnerDB::addRunner(const char *nameUTF,
en.cardNo = card; en.cardNo = card;
en.clubNo = club; en.clubNo = club;
e.setNameUTF(nameUTF); e.setNameUTF(nameUTF);
en.extId = extId; en.setExtId(extId);
if (!check(en) ) { if (!check(en) ) {
rdb.pop_back(); rdb.pop_back();
@ -550,16 +550,16 @@ RunnerWDBEntry *RunnerDB::getRunnerByName(const wstring &name, int clubId,
int bestYear = 0; int bestYear = 0;
for (size_t k = 0;k<ix2.size(); k++) { for (size_t k = 0;k<ix2.size(); k++) {
const RunnerWDBEntry &re = rwdb[ix2[k]]; const RunnerWDBEntry &re = rwdb[ix2[k]];
if (abs(re.dbe().birthYear-expectedBirthYear) < abs(bestYear-expectedBirthYear)) { if (abs(re.dbe().getBirthDay() - expectedBirthYear) < abs(bestYear - expectedBirthYear)) {
bestMatch = ix2[k]; bestMatch = ix2[k];
bestYear = re.dbe().birthYear; bestYear = re.dbe().getBirthYear();
} }
} }
if (bestYear>0) if (bestYear>0)
return (RunnerWDBEntry *)&rwdb[bestMatch]; return (RunnerWDBEntry *)&rwdb[bestMatch];
} }
return 0; return nullptr;
} }
void RunnerDB::setupIdHash() const void RunnerDB::setupIdHash() const
@ -569,7 +569,7 @@ void RunnerDB::setupIdHash() const
for (size_t k=0; k<rdb.size(); k++) { for (size_t k=0; k<rdb.size(); k++) {
if (!rdb[k].isRemoved()) if (!rdb[k].isRemoved())
idhash[rdb[k].extId] = int(k); idhash[rdb[k].getExtId()] = int(k);
} }
} }
@ -1076,13 +1076,13 @@ void RunnerDB::updateAdd(const oRunner &r, map<int, int> &clubIdMap)
if (dbe == nullptr) { if (dbe == nullptr) {
dbe = addRunner(r.getName().c_str(), 0, localClubId, r.getCardNo()); dbe = addRunner(r.getName().c_str(), 0, localClubId, r.getCardNo());
if (dbe) if (dbe)
dbe->dbe().birthYear = r.getDCI().getInt("BirthYear"); dbe->dbe().setBirthYear(r.getDCI().getInt("BirthYear"));
} }
else { else {
if (dbe->getExtId() == 0) { // Only update entries not in national db. if (dbe->getExtId() == 0) { // Only update entries not in national db.
dbe->setName(r.getName().c_str()); dbe->setName(r.getName().c_str());
dbe->dbe().clubNo = localClubId; dbe->dbe().clubNo = localClubId;
dbe->dbe().birthYear = r.getDCI().getInt("BirthYear"); dbe->dbe().setBirthYear(r.getDCI().getInt("BirthYear"));
} }
} }
} }
@ -1251,13 +1251,13 @@ const shared_ptr<Table> &RunnerDB::getRunnerTB() {
auto table = make_shared<Table>(oe, 20, L"Löpardatabasen", "runnerdb"); auto table = make_shared<Table>(oe, 20, L"Löpardatabasen", "runnerdb");
table->addColumn("Index", 70, true, true); table->addColumn("Index", 70, true, true);
table->addColumn("Id", 70, true, true); table->addColumn("Externt Id", 70, true, true);
table->addColumn("Namn", 200, false); table->addColumn("Namn", 200, false);
table->addColumn("Klubb", 200, false); table->addColumn("Klubb", 200, false);
table->addColumn("SI", 70, true, true); table->addColumn("SI", 70, true, true);
table->addColumn("Nationalitet", 70, false, true); table->addColumn("Nationalitet", 70, false, true);
table->addColumn("Kön", 50, false, true); table->addColumn("Kön", 50, false, true);
table->addColumn("Födelseår", 70, true, true); table->addColumn("RunnerBirthDate", 70, true, true);
table->addColumn("Anmäl", 120, false, true); table->addColumn("Anmäl", 120, false, true);
table->setTableProp(Table::CAN_INSERT|Table::CAN_DELETE|Table::CAN_PASTE); table->setTableProp(Table::CAN_INSERT|Table::CAN_DELETE|Table::CAN_PASTE);
@ -1330,8 +1330,8 @@ void RunnerDB::refreshRunnerTableData(Table &table) {
bool found = false; bool found = false;
pRunner r = nullptr; pRunner r = nullptr;
if (rdb[k].extId != 0) if (rdb[k].getExtId() != 0)
found = runnerInEvent.lookup(rdb[k].extId, runnerId); found = runnerInEvent.lookup(rdb[k].getExtId(), runnerId);
else if (rdb[k].cardNo != 0) { else if (rdb[k].cardNo != 0) {
found = runnerInEvent.lookup(rdb[k].cardNo + cardIdConstant, runnerId); found = runnerInEvent.lookup(rdb[k].cardNo + cardIdConstant, runnerId);
if (found) { if (found) {
@ -1404,8 +1404,8 @@ void oDBRunnerEntry::addTableRow(Table &table) const {
table.set(row++, it, TID_INDEX, itow(index+1), false, cellEdit); table.set(row++, it, TID_INDEX, itow(index+1), false, cellEdit);
wchar_t bf[16]; wchar_t bf[16];
oBase::converExtIdentifierString(rn.extId, bf); oBase::converExtIdentifierString(rn.getExtId(), bf);
table.set(row++, it, TID_ID, bf, false, cellEdit); table.set(row++, it, TID_ID, bf, canEdit, cellEdit);
r.initName(); r.initName();
table.set(row++, it, TID_NAME, r.name, canEdit, cellEdit); table.set(row++, it, TID_NAME, r.name, canEdit, cellEdit);
@ -1421,14 +1421,14 @@ void oDBRunnerEntry::addTableRow(Table &table) const {
table.set(row++, it, TID_NATIONAL, nat, canEdit, cellEdit); table.set(row++, it, TID_NATIONAL, nat, canEdit, cellEdit);
wchar_t sex[2] = {wchar_t(rn.sex), 0}; wchar_t sex[2] = {wchar_t(rn.sex), 0};
table.set(row++, it, TID_SEX, sex, canEdit, cellEdit); table.set(row++, it, TID_SEX, sex, canEdit, cellEdit);
table.set(row++, it, TID_YEAR, itow(rn.birthYear), canEdit, cellEdit); table.set(row++, it, TID_YEAR, rn.getBirthDate(), canEdit, cellEdit);
int runnerId; int runnerId;
bool found = false; bool found = false;
pRunner cr = nullptr; pRunner cr = nullptr;
if (rn.extId != 0) if (rn.getExtId() != 0)
found = db->runnerInEvent.lookup(rn.extId, runnerId); found = db->runnerInEvent.lookup(rn.getExtId(), runnerId);
else if (rn.cardNo != 0) { else if (rn.cardNo != 0) {
found = db->runnerInEvent.lookup(rn.cardNo + cardIdConstant, runnerId); found = db->runnerInEvent.lookup(rn.cardNo + cardIdConstant, runnerId);
if (found) { if (found) {
@ -1467,8 +1467,29 @@ pair<int, bool> oDBRunnerEntry::inputData(int id, const wstring &input,
throw meosException("Not initialized"); throw meosException("Not initialized");
RunnerWDBEntry &r = db->rwdb[index]; RunnerWDBEntry &r = db->rwdb[index];
RunnerDBEntry &rd = db->rdb[index]; RunnerDBEntry &rd = db->rdb[index];
static bool hasWarnedId = false;
switch(id) { switch(id) {
case TID_ID: {
wchar_t bf[16];
auto key = oBase::converExtIdentifierString(input);
oBase::converExtIdentifierString(key, bf);
if (compareStringIgnoreCase(bf, input)) {
throw meosException(L"Cannot represent ID X#" + input);
}
if (key != r.getExtId() && !hasWarnedId) {
if (oe->gdiBase().askOkCancel(L"warn:changeid") == gdioutput::AskAnswer::AnswerCancel)
throw meosCancel();
hasWarnedId = true;
}
r.setExtId(key);
db->idhash.clear();
output = bf;
}
break;
case TID_NAME: case TID_NAME:
r.setName(input.c_str()); r.setName(input.c_str());
r.getName(output); r.getName(output);
@ -1503,8 +1524,8 @@ pair<int, bool> oDBRunnerEntry::inputData(int id, const wstring &input,
output = r.getSex(); output = r.getSex();
break; break;
case TID_YEAR: case TID_YEAR:
rd.birthYear = short(_wtoi(input.c_str())); rd.setBirthDate(input);
output = itow(r.getBirthYear()); output = rd.getBirthDate();
break; break;
case TID_CLUB: case TID_CLUB:
@ -1529,7 +1550,7 @@ void oDBRunnerEntry::fillInput(int id, vector< pair<wstring, size_t> > &out, siz
void oDBRunnerEntry::remove() { void oDBRunnerEntry::remove() {
RunnerWDBEntry &r = db->rwdb[index]; RunnerWDBEntry &r = db->rwdb[index];
r.remove(); r.remove();
db->idhash.remove(r.dbe().extId); db->idhash.remove(r.dbe().getExtId());
wstring cname(canonizeName(r.name)); wstring cname(canonizeName(r.name));
multimap<wstring, int>::const_iterator it = db->nhash.find(cname); multimap<wstring, int>::const_iterator it = db->nhash.find(cname);
@ -1823,6 +1844,20 @@ vector<pair<RunnerWDBEntry *, int>> RunnerDB::getRunnerSuggestions(const wstring
else else
setupAutoCompleteHash(AutoHashMode::Runners); setupAutoCompleteHash(AutoHashMode::Runners);
// Check if database key
int64_t id = 0;
for (int j = 0; j < key.length(); j++) {
if (key[j] >= '0' && key[j] <= '9') {
id = oBase::converExtIdentifierString(key);
wchar_t bf[16];
oBase::converExtIdentifierString(id, bf);
if (compareStringIgnoreCase(key, bf))
id = 0;
break;
}
}
vector< pair<int, int> > outOrder; vector< pair<int, int> > outOrder;
set<pair<int, int>> ix; set<pair<int, int>> ix;
wchar_t bf[256]; wchar_t bf[256];
@ -1866,8 +1901,15 @@ vector<pair<RunnerWDBEntry *, int>> RunnerDB::getRunnerSuggestions(const wstring
if (res != runnerHash.end()) if (res != runnerHash.end())
res->second.match(*this, ix, nameParts); res->second.match(*this, ix, nameParts);
} }
} }
if (id > 0) {
auto r = getRunnerById(id);
if (r) {
ix.emplace(1000, r->getIndex());
}
}
if (ix.empty()) if (ix.empty())
return ret; return ret;
@ -1896,3 +1938,85 @@ vector<pair<RunnerWDBEntry *, int>> RunnerDB::getRunnerSuggestions(const wstring
} }
return ret; return ret;
} }
const wstring& RunnerDBEntry::getBirthDate() const {
int year = getBirthYear();
if (year <= 0 || year>9999)
return _EmptyWString;
int month = getBirthMonth();
if (month > 0 && month <= 12) {
int day = getBirthDay();
if (day > 0 && day <= 31) {
wchar_t bf[16];
swprintf_s(bf, L"%d-%02d-%02d", year, month, day);
wstring& res = StringCache::getInstance().wget();
res = bf;
return res;
}
}
return itow(year);
}
void RunnerDBEntry::setBirthDate(const wstring& in) {
SYSTEMTIME st;
if (convertDateYMS(in, st, true) > 0) {
setBirthYear(st.wYear);
setBirthMonth(st.wMonth);
setBirthDay(st.wDay);
}
else {
int year = _wtoi(in.c_str());
if (year > 1900 && year < 9999)
setBirthYear(year);
else
setBirthYear(0);
setBirthMonth(0);
setBirthDay(0);
}
}
void RunnerDBEntry::setBirthDate(int dateOrYear) {
if (dateOrYear > 0 && dateOrYear < 100)
dateOrYear = extendYear(dateOrYear);
if ((dateOrYear > 1900 && dateOrYear < 9999) || dateOrYear == 0) {
setBirthYear(dateOrYear);
setBirthMonth(0);
setBirthDay(0);
return;
}
int d = dateOrYear % 100;
dateOrYear /= 100;
int m = dateOrYear % 100;
dateOrYear /= 100;
int y = extendYear(dateOrYear);
if (d > 0 && d <= 31 && m > 0 && m <= 12 && y > 1900 && y < 9999) {
setBirthYear(y);
setBirthMonth(m);
setBirthDay(d);
}
else if (y > 1900 && y < 9999) {
setBirthYear(y);
}
else {
setBirthYear(0);
setBirthMonth(0);
setBirthDay(0);
}
}
int RunnerDBEntry::getBirthDateInt() const {
int y = getBirthYear();
int m = getBirthMonth();
int d = getBirthDay();
if (y > 0 && y < 9999 && m > 0 && d > 0)
return y * 10000 + m * 100 + d;
else
return y;
}

View File

@ -11,7 +11,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -97,11 +97,13 @@ struct RunnerDBEntry {
int clubNo; int clubNo;
char national[3]; char national[3];
char sex; char sex;
private:
short int birthYear; short int birthYear;
short int reserved; short int reserved;
/** End of V1*/ /** End of V1*/
__int64 extId; __int64 extId;
public:
bool isRemoved() const { return (reserved & 1) == 1; } bool isRemoved() const { return (reserved & 1) == 1; }
void remove() { reserved |= 1; } void remove() { reserved |= 1; }
@ -111,6 +113,22 @@ struct RunnerDBEntry {
bool operator==(const RunnerDBEntry &d) const { bool operator==(const RunnerDBEntry &d) const {
return memcmp(this, &d, sizeof(RunnerDBEntry)) == 0; return memcmp(this, &d, sizeof(RunnerDBEntry)) == 0;
} }
int getBirthYear() const { return birthYear; }
int getBirthMonth() const { return (reserved >> 2) & 0xF; }// 4 bits
int getBirthDay() const { return (reserved >> 6) & 0x1F; } // 5 bits
void setBirthYear(int year) { birthYear = year; }
void setBirthMonth(int month) { reserved = (reserved & (~(0xF << 2))) | ((0xF & month) << 2); }// 4 bits
void setBirthDay(int day) { reserved = (reserved & (~(0x1F << 6))) | ((0x1F & day) << 6); } // 5 bits
const wstring& getBirthDate() const;
void setBirthDate(const wstring& in);
void setBirthDate(int dateOrYear);
int getBirthDateInt() const;
void setExtId(__int64 id) { extId = id; }
__int64 getExtId() const { return extId; }
}; };
class RunnerDB; class RunnerDB;
@ -125,6 +143,8 @@ public:
void init(RunnerDB *p, size_t ix); void init(RunnerDB *p, size_t ix);
RunnerWDBEntry(); RunnerWDBEntry();
size_t getIndex() const { return ix; }
// Link to narrow DB Entry // Link to narrow DB Entry
const RunnerDBEntry &dbe() const; const RunnerDBEntry &dbe() const;
RunnerDBEntry &dbe(); RunnerDBEntry &dbe();
@ -145,14 +165,14 @@ public:
wstring getFamilyName() const; wstring getFamilyName() const;
wstring getNationality() const; wstring getNationality() const;
int getBirthYear() const {return dbe().birthYear;} int getBirthYear() const {return dbe().getBirthYear(); }
wstring getSex() const; wstring getSex() const;
__int64 getExtId() const; __int64 getExtId() const;
void setExtId(__int64 id); void setExtId(__int64 id);
bool isRemoved() const {return (dbe().reserved & 1) == 1;} bool isRemoved() const { return dbe().isRemoved(); }
void remove() {dbe().reserved |= 1;} void remove() { dbe().remove(); }
}; };
typedef vector<RunnerDBEntry> RunnerDBVector; typedef vector<RunnerDBEntry> RunnerDBVector;

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -46,11 +46,11 @@
//#define DEBUG_SI //#define DEBUG_SI
SI_StationData::SI_StationData() { SI_StationData::SI_StationData() {
stationNumber=0; stationNumber = 0;
stationMode=0; stationMode = 0;
extended=false; extended = false;
handShake=false; handShake = false;
autoSend=false; autoSend = false;
radioChannel = 0; radioChannel = 0;
} }
@ -64,19 +64,28 @@ SI_StationInfo::SI_StationInfo()
localZeroTime=0; localZeroTime=0;
} }
SportIdent::SportIdent(HWND hWnd, DWORD Id, bool readVoltage) : readVoltage(readVoltage) SportIdent::SportIdent(HWND hWnd, DWORD Id, bool readVoltage) : readVoltage(readVoltage) {
{ ClassId = Id;
ClassId=Id; hWndNotify = hWnd;
hWndNotify=hWnd;
//hComm=0; n_SI_Info = 0;
//ThreadHandle=0;
n_SI_Info=0;
InitializeCriticalSection(&SyncObj); InitializeCriticalSection(&SyncObj);
tcpPortOpen=0; tcpPortOpen = 0;
serverSocket=0; serverSocket = 0;
punchMap.resize(31, 0);
punchMap[oPunch::SpecialPunch::PunchStart] = oPunch::SpecialPunch::PunchStart;
punchMap[oPunch::SpecialPunch::PunchCheck] = oPunch::SpecialPunch::PunchCheck;
punchMap[oPunch::SpecialPunch::PunchFinish] = oPunch::SpecialPunch::PunchFinish;
}
void SportIdent::resetPunchMap() {
punchMap.resize(31, 0);
fill(punchMap.begin(), punchMap.end(), 0);
punchMap[oPunch::SpecialPunch::PunchStart] = oPunch::SpecialPunch::PunchStart;
punchMap[oPunch::SpecialPunch::PunchCheck] = oPunch::SpecialPunch::PunchCheck;
punchMap[oPunch::SpecialPunch::PunchFinish] = oPunch::SpecialPunch::PunchFinish;
} }
SportIdent::~SportIdent() SportIdent::~SportIdent()
@ -299,7 +308,7 @@ string decode(BYTE *bf, int read)
bool SportIdent::openComListen(const wchar_t *com, DWORD BaudRate) { bool SportIdent::openComListen(const wchar_t *com, DWORD BaudRate) {
closeCom(com); closeCom(com);
SI_StationInfo *si = findStation(com); SI_StationInfo *si = findStationInt(com);
if (!si) { if (!si) {
SI_Info[n_SI_Info].ComPort=com; SI_Info[n_SI_Info].ComPort=com;
@ -309,6 +318,9 @@ bool SportIdent::openComListen(const wchar_t *com, DWORD BaudRate) {
} }
si->data.clear(); si->data.clear();
if (si->ComPort == L"TEST")
return true; // Passive test
wstring comfile=wstring(L"//./")+com; wstring comfile=wstring(L"//./")+com;
si->hComm = CreateFile( comfile.c_str(), si->hComm = CreateFile( comfile.c_str(),
GENERIC_READ | GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE,
@ -355,7 +367,7 @@ bool SportIdent::tcpAddPort(int port, DWORD zeroTime)
{ {
closeCom(L"TCP"); closeCom(L"TCP");
SI_StationInfo *si = findStation(L"TCP"); SI_StationInfo *si = findStationInt(L"TCP");
if (!si) { if (!si) {
SI_Info[n_SI_Info].ComPort=L"TCP"; SI_Info[n_SI_Info].ComPort=L"TCP";
@ -374,7 +386,7 @@ bool SportIdent::openCom(const wchar_t *com)
{ {
closeCom(com); closeCom(com);
SI_StationInfo *si = findStation(com); SI_StationInfo *si = findStationInt(com);
if (!si) { if (!si) {
SI_Info[n_SI_Info].ComPort=com; SI_Info[n_SI_Info].ComPort=com;
@ -386,7 +398,7 @@ bool SportIdent::openCom(const wchar_t *com)
si->data.clear(); si->data.clear();
if (si->ComPort == L"TEST") { if (si->ComPort == L"TEST") {
return true; return false;
} }
wstring comfile=wstring(L"//./")+com; wstring comfile=wstring(L"//./")+com;
@ -511,9 +523,7 @@ bool SportIdent::openCom(const wchar_t *com)
return true; return true;
} }
SI_StationInfo* SportIdent::findStationInt(const wstring& com) {
SI_StationInfo *SportIdent::findStation(const wstring &com)
{
if (com == L"TEST" && n_SI_Info < 30) { if (com == L"TEST" && n_SI_Info < 30) {
if (n_SI_Info == 0 || SI_Info[n_SI_Info - 1].ComPort != com) { if (n_SI_Info == 0 || SI_Info[n_SI_Info - 1].ComPort != com) {
SI_Info[n_SI_Info].ComPort = com; SI_Info[n_SI_Info].ComPort = com;
@ -521,6 +531,26 @@ SI_StationInfo *SportIdent::findStation(const wstring &com)
} }
} }
for (int i = 0; i < n_SI_Info; i++)
if (com == SI_Info[i].ComPort)
return &SI_Info[i];
return 0;
}
void SportIdent::addTestStation(const wstring& com) {
SI_Info[n_SI_Info].ComPort = com;
n_SI_Info++;
}
const SI_StationInfo *SportIdent::findStation(const wstring &com) const
{
if (com == L"TEST" && n_SI_Info < 30) {
if (n_SI_Info == 0 || SI_Info[n_SI_Info - 1].ComPort != com) {
const_cast<SportIdent*>(this)->addTestStation(com);
}
}
for(int i=0;i<n_SI_Info; i++) for(int i=0;i<n_SI_Info; i++)
if (com == SI_Info[i].ComPort) if (com == SI_Info[i].ComPort)
return &SI_Info[i]; return &SI_Info[i];
@ -537,7 +567,7 @@ void SportIdent::closeCom(const wchar_t *com)
} }
else else
{ {
SI_StationInfo *si = findStation(com); SI_StationInfo *si = findStationInt(com);
if (si && si->ComPort==L"TCP") { if (si && si->ComPort==L"TCP") {
if (tcpPortOpen) { if (tcpPortOpen) {
@ -890,7 +920,7 @@ int SportIdent::MonitorTCPSI(WORD port, int localZeroTime)
} }
} }
else else
addPunch(op.CodeTime/10, op.CodeNo, op.SICardNo, 0); addPunch(op.CodeTime, op.CodeNo, op.SICardNo, 0);
} }
else r=-1; else r=-1;
@ -933,10 +963,10 @@ bool SportIdent::MonitorTEST(SI_StationInfo &si)
SICard card(ConvertedTimeStatus::Hour12); SICard card(ConvertedTimeStatus::Hour12);
card.StartPunch.Code = 1; card.StartPunch.Code = 1;
int t = card.StartPunch.Time = 3600*8 + rand()%1000; int t = card.StartPunch.Time = timeConstHour*8 + rand()%1000;
card.FinishPunch.Code = 2; card.FinishPunch.Code = 2;
card.FinishPunch.Time = card.StartPunch.Time + 1800 + rand() % 3600; card.FinishPunch.Time = card.StartPunch.Time + timeConstHour/2 + rand() % timeConstHour;
card.CardNumber = tc.cardNo; card.CardNumber = tc.cardNo;
for (size_t k = 0; k < tc.punches.size(); k++) { for (size_t k = 0; k < tc.punches.size(); k++) {
@ -947,8 +977,8 @@ bool SportIdent::MonitorTEST(SI_StationInfo &si)
} }
addCard(card); addCard(card);
//Sleep(300 + rand()%600); Sleep(300 + rand()%600);
Sleep(0); //Sleep(0);
if (++longSleepIter > 20) { if (++longSleepIter > 20) {
Sleep(100 + rand() % 600); Sleep(100 + rand() % 600);
longSleepIter = 0; longSleepIter = 0;
@ -1002,12 +1032,15 @@ bool SportIdent::MonitorSI(SI_StationInfo &si)
DWORD Card=MAKELONG(MAKEWORD(bf[7], bf[6]), MAKEWORD(bf[5], bf[4])); DWORD Card=MAKELONG(MAKEWORD(bf[7], bf[6]), MAKEWORD(bf[5], bf[4]));
if (Series<=4 && Series>=1) if (Series <= 4 && Series >= 1)
Card=ShortCard+100000*Series; Card = ShortCard + 100000 * Series;
DWORD Time=0; DWORD Time=0;
if (bf[8]&0x1) Time=3600*12; if (bf[8] & 0x1) Time = timeConstHour * 12;
Time+=MAKEWORD(bf[10], bf[9]); Time += MAKEWORD(bf[10], bf[9])*timeConstSecond;
uint8_t tss = bf[11]; // Sub second 1/256 seconds
int tenth = (((100 * tss) / 256) + 4) / 10;
Time += tenth;
#ifdef DEBUG_SI #ifdef DEBUG_SI
char str[128]; char str[128];
sprintf_s(str, "EXTENDED: Card = %d, Station = %d, StationMode = %d", Card, Station, si.StationMode); sprintf_s(str, "EXTENDED: Card = %d, Station = %d, StationMode = %d", Card, Station, si.StationMode);
@ -1040,9 +1073,9 @@ bool SportIdent::MonitorSI(SI_StationInfo &si)
//if (Series!=1) //if (Series!=1)
// Card+=100000*Series; // Card+=100000*Series;
DWORD Time=MAKEWORD(bf[8], bf[7]); DWORD Time = MAKEWORD(bf[8], bf[7]) * timeConstSecond;
BYTE p=bf[1]; BYTE p=bf[1];
if (p&0x1) Time+=3600*12; if (p&0x1) Time+=timeConstHour*12;
#ifdef DEBUG_SI #ifdef DEBUG_SI
char str[128]; char str[128];
@ -1100,7 +1133,7 @@ bool SportIdent::MonitorSI(SI_StationInfo &si)
bf[0]=chRead; bf[0]=chRead;
readBytes(bf+1, 200, hComm); readBytes(bf+1, 200, hComm);
//GetSI5DataExt(hComm); //GetSI5DataExt(hComm);
MessageBox(NULL, L"Programmera stationen utan AUTOSEND!", NULL, MB_OK); MessageBox(NULL, lang.tl(L"Programmera stationen utan AUTOSEND").c_str(), NULL, MB_OK);
} }
break; break;
@ -1108,7 +1141,7 @@ bool SportIdent::MonitorSI(SI_StationInfo &si)
BYTE bf[200]; BYTE bf[200];
bf[0]=chRead; bf[0]=chRead;
readBytes(bf+1, 200, hComm); readBytes(bf+1, 200, hComm);
MessageBox(NULL, L"Programmera stationen utan AUTOSEND!", NULL, MB_OK); MessageBox(NULL, lang.tl(L"Programmera stationen utan AUTOSEND").c_str(), NULL, MB_OK);
} }
break; break;
case 0xE8:{ case 0xE8:{
@ -1125,7 +1158,6 @@ bool SportIdent::MonitorSI(SI_StationInfo &si)
// MessageBox(NULL, "SI-card not supported", NULL, MB_OK); // MessageBox(NULL, "SI-card not supported", NULL, MB_OK);
// break; // break;
default: default:
BYTE bf[128]; BYTE bf[128];
bf[0]=chRead; bf[0]=chRead;
int rb=readBytes(bf+1, 120, hComm); int rb=readBytes(bf+1, 120, hComm);
@ -1146,6 +1178,9 @@ bool SportIdent::MonitorSI(SI_StationInfo &si)
st+=d; st+=d;
} }
} }
if (chRead == 0xEF)
MessageBox(NULL, lang.tl(L"Programmera stationen utan AUTOSEND").c_str(), NULL, MB_OK);
//MessageBox(NULL, st.c_str(), "Unknown SI response", MB_OK); //MessageBox(NULL, st.c_str(), "Unknown SI response", MB_OK);
} }
} }
@ -1650,8 +1685,6 @@ bool SportIdent::getCard5Data(BYTE *data, SICard &card)
analyseSI5Time(data+5, card.FinishPunch.Time, card.FinishPunch.Code); analyseSI5Time(data+5, card.FinishPunch.Time, card.FinishPunch.Code);
analyseSI5Time(data+9, card.CheckPunch.Time, card.CheckPunch.Code); analyseSI5Time(data+9, card.CheckPunch.Time, card.CheckPunch.Code);
// card.StartPunch=MAKEWORD(data[4], data[3]);
// card.FinishPunch=MAKEWORD(data[6], data[5]);
card.nPunch=data[7]-1; card.nPunch=data[7]-1;
data+=16; data+=16;
@ -1660,7 +1693,7 @@ bool SportIdent::getCard5Data(BYTE *data, SICard &card)
if (k<30) { if (k<30) {
DWORD basepointer=3*(k%5)+1+(k/5)*16; DWORD basepointer=3*(k%5)+1+(k/5)*16;
DWORD code=data[basepointer]; DWORD code=data[basepointer];
DWORD time;//=MAKEWORD(data[basepointer+2],data[basepointer+1]); DWORD time;
DWORD slask; DWORD slask;
analyseSI5Time(data+basepointer+1, time, slask); analyseSI5Time(data+basepointer+1, time, slask);
@ -1710,29 +1743,29 @@ bool SportIdent::getCard9Data(BYTE *data, SICard &card)
int series = data[24] & 15; int series = data[24] & 15;
card.convertedTime = ConvertedTimeStatus::Hour24; card.convertedTime = ConvertedTimeStatus::Hour24;
analysePunch(data+12, card.StartPunch.Time, card.StartPunch.Code); analysePunch(data+12, card.StartPunch.Time, card.StartPunch.Code, useSubsecondMode);
analysePunch(data+16, card.FinishPunch.Time, card.FinishPunch.Code); analysePunch(data+16, card.FinishPunch.Time, card.FinishPunch.Code, useSubsecondMode);
analysePunch(data+8, card.CheckPunch.Time, card.CheckPunch.Code); analysePunch(data+8, card.CheckPunch.Time, card.CheckPunch.Code, false);
if (series == 1) { if (series == 1) {
// SI Card 9 // SI Card 9
card.nPunch=min(int(data[22]), 50); card.nPunch=min(int(data[22]), 50);
for(unsigned k=0;k<card.nPunch;k++) { for(unsigned k=0;k<card.nPunch;k++) {
analysePunch(14*4 + data + 4*k, card.Punch[k].Time, card.Punch[k].Code); analysePunch(14*4 + data + 4*k, card.Punch[k].Time, card.Punch[k].Code, false);
} }
} }
else if (series == 2) { else if (series == 2) {
// SI Card 8 // SI Card 8
card.nPunch=min(int(data[22]), 30); card.nPunch=min(int(data[22]), 30);
for(unsigned k=0;k<card.nPunch;k++) { for(unsigned k=0;k<card.nPunch;k++) {
analysePunch(34*4 + data + 4*k, card.Punch[k].Time, card.Punch[k].Code); analysePunch(34*4 + data + 4*k, card.Punch[k].Time, card.Punch[k].Code, false);
} }
} }
else if (series == 4) { else if (series == 4) {
// pCard // pCard
card.nPunch=min(int(data[22]), 20); card.nPunch=min(int(data[22]), 20);
for(unsigned k=0;k<card.nPunch;k++) { for(unsigned k=0;k<card.nPunch;k++) {
analysePunch(44*4 + data + 4*k, card.Punch[k].Time, card.Punch[k].Code); analysePunch(44*4 + data + 4*k, card.Punch[k].Time, card.Punch[k].Code, false);
} }
} }
else if (series == 6) { else if (series == 6) {
@ -1751,7 +1784,7 @@ bool SportIdent::getCard9Data(BYTE *data, SICard &card)
// Card 10, 11, SIAC // Card 10, 11, SIAC
card.nPunch=min(int(data[22]), 128); card.nPunch=min(int(data[22]), 128);
for(unsigned k=0;k<card.nPunch;k++) { for(unsigned k=0;k<card.nPunch;k++) {
analysePunch(data + 128 + 4*k, card.Punch[k].Time, card.Punch[k].Code); analysePunch(data + 128 + 4*k, card.Punch[k].Time, card.Punch[k].Code, false);
} }
} }
else else
@ -1774,7 +1807,7 @@ void SportIdent::analyseTPunch(BYTE *data, DWORD &time, DWORD &control) {
// BYTE day = (dt0 >> 1) & 0x1F; // BYTE day = (dt0 >> 1) & 0x1F;
control=cn; control=cn;
time=MAKEWORD(ptl, pth)+3600*12*(dt0&0x1); time = MAKEWORD(ptl, pth) * timeConstSecond + timeConstHour * 12 * (dt0 & 0x1);
} }
else { else {
control=-1; control=-1;
@ -1810,9 +1843,9 @@ bool SportIdent::getCard6Data(BYTE *data, SICard &card)
// DWORD control; // DWORD control;
// DWORD time; // DWORD time;
card.convertedTime = ConvertedTimeStatus::Hour24; card.convertedTime = ConvertedTimeStatus::Hour24;
analysePunch(data+8, card.StartPunch.Time, card.StartPunch.Code); analysePunch(data+8, card.StartPunch.Time, card.StartPunch.Code, useSubsecondMode);
analysePunch(data+4, card.FinishPunch.Time, card.FinishPunch.Code); analysePunch(data+4, card.FinishPunch.Time, card.FinishPunch.Code, useSubsecondMode);
analysePunch(data+12, card.CheckPunch.Time, card.CheckPunch.Code); analysePunch(data+12, card.CheckPunch.Time, card.CheckPunch.Code, false);
card.nPunch=min(int(data[2]), 192); card.nPunch=min(int(data[2]), 192);
int i; int i;
@ -1843,12 +1876,12 @@ bool SportIdent::getCard6Data(BYTE *data, SICard &card)
data+=128-16; data+=128-16;
for(unsigned k=0;k<card.nPunch;k++) { for(unsigned k=0;k<card.nPunch;k++) {
analysePunch(data+4*k, card.Punch[k].Time, card.Punch[k].Code); analysePunch(data+4*k, card.Punch[k].Time, card.Punch[k].Code, false);
} }
// Check for extra punches, SI6-bug // Check for extra punches, SI6-bug
for (unsigned k = card.nPunch; k < 192; k++) { for (unsigned k = card.nPunch; k < 192; k++) {
if (!analysePunch(data+4*k, card.Punch[k].Time, card.Punch[k].Code)) { if (!analysePunch(data+4*k, card.Punch[k].Time, card.Punch[k].Code, false)) {
break; break;
} }
else { else {
@ -1860,7 +1893,7 @@ bool SportIdent::getCard6Data(BYTE *data, SICard &card)
return true; return true;
} }
bool SportIdent::analysePunch(BYTE *data, DWORD &time, DWORD &control) { bool SportIdent::analysePunch(BYTE *data, DWORD &time, DWORD &control, bool subSecond) {
if (*LPDWORD(data)!=0xEEEEEEEE && *LPDWORD(data)!=0x0) if (*LPDWORD(data)!=0xEEEEEEEE && *LPDWORD(data)!=0x0)
{ {
BYTE ptd=data[0]; BYTE ptd=data[0];
@ -1868,8 +1901,16 @@ bool SportIdent::analysePunch(BYTE *data, DWORD &time, DWORD &control) {
BYTE pth=data[2]; BYTE pth=data[2];
BYTE ptl=data[3]; BYTE ptl=data[3];
control=cn+256*((ptd>>6)&0x3); time = timeConstSecond * MAKEWORD(ptl, pth) + timeConstHour * 12 * (ptd & 0x1);
time=MAKEWORD(ptl, pth)+3600*12*(ptd&0x1); if (!subSecond) {
control = cn + 256 * ((ptd >> 6) & 0x3);
}
else {
control = 0;
uint8_t tss = data[1]; // Sub second 1/256 seconds
int tenth = (((100 * tss) / 256) + 4) / 10;
time += tenth;
}
return true; return true;
} }
else else
@ -1880,15 +1921,15 @@ bool SportIdent::analysePunch(BYTE *data, DWORD &time, DWORD &control) {
} }
} }
void SportIdent::analyseSI5Time(BYTE *data, DWORD &time, DWORD &control) void SportIdent::analyseSI5Time(BYTE* data, DWORD& time, DWORD& control)
{ {
if (*LPWORD(data)!=0xEEEE) { if (*LPWORD(data) != 0xEEEE) {
time=MAKEWORD(data[1], data[0]); time = MAKEWORD(data[1], data[0]) * timeConstSecond;
control=0; control = 0;
} }
else { else {
control=-1; control = -1;
time=0; time = 0;
} }
} }
@ -1906,17 +1947,17 @@ void SICard::analyseHour12Time(DWORD zeroTime) {
} }
void SIPunch::analyseHour12Time(DWORD zeroTime) { void SIPunch::analyseHour12Time(DWORD zeroTime) {
if (Code != -1 && Time>=0 && Time <=12*3600) { if (Code != -1 && Time>=0 && Time <=12*timeConstHour) {
if (zeroTime < 12 * 3600) { if (zeroTime < 12 * timeConstHour) {
//Förmiddag //Förmiddag
if (Time < zeroTime) if (Time < zeroTime)
Time += 12 * 3600; //->Eftermiddag Time += 12 * timeConstHour; //->Eftermiddag
} }
else { else {
//Eftermiddag //Eftermiddag
if (Time >= zeroTime % (12 * 3600)) { if (Time >= zeroTime % (12 * timeConstHour)) {
//Eftermiddag //Eftermiddag
Time += 12 * 3600; Time += 12 * timeConstHour;
} }
// else Efter midnatt OK. // else Efter midnatt OK.
} }
@ -1985,50 +2026,67 @@ void SportIdent::addCard(const SICard &sic)
PostMessage(hWndNotify, WM_USER, ClassId, 0); PostMessage(hWndNotify, WM_USER, ClassId, 0);
} }
void SportIdent::addPunch(DWORD Time, int Station, int Card, int Mode) void SportIdent::addPunch(DWORD Time, int Station, int Card, int Mode) {
{ if (!useSubsecondMode)
Time -= (Time % timeConstSecond);
SICard sic(ConvertedTimeStatus::Hour24); SICard sic(ConvertedTimeStatus::Hour24);
sic.CardNumber=Card; sic.CardNumber = Card;
sic.StartPunch.Code = -1; sic.StartPunch.Code = -1;
sic.CheckPunch.Code = -1; sic.CheckPunch.Code = -1;
sic.FinishPunch.Code = -1; sic.FinishPunch.Code = -1;
if (Mode==0 || Mode == 11){ // 11 is dongle auto mapPunch = [this](int code) {
if (Station>30){ if (code > 0 && code < punchMap.size() && punchMap[code] > 0)
sic.Punch[0].Code=Station; return punchMap[code];
sic.Punch[0].Time=Time; else
sic.nPunch=1; return code;
};
if (Mode == 0 || Mode == 11) { // 11 is dongle
int code = (Station & 0xFFFF);
int mappedCode = mapPunch(code);
int unit = 0;
if (mappedCode != code)
unit = code;
else
unit = (Station >> 16) & 0xFFFF;
if (mappedCode > 30) {
sic.Punch[0].Code = Station;
sic.Punch[0].Time = Time;
sic.nPunch = 1;
} }
else if (Station == oPunch::PunchStart) { else if (mappedCode == oPunch::PunchStart) {
sic.StartPunch.Time = Time; sic.StartPunch.Time = Time;
sic.StartPunch.Code = oPunch::PunchStart; sic.StartPunch.Code = unit;
} }
else if (Station == oPunch::PunchCheck) { else if (mappedCode == oPunch::PunchCheck) {
sic.CheckPunch.Time = Time; sic.CheckPunch.Time = Time;
sic.CheckPunch.Code = oPunch::PunchCheck; sic.CheckPunch.Code = unit;
} }
else{ else {
sic.FinishPunch.Time=Time; sic.FinishPunch.Time = Time;
sic.FinishPunch.Code = oPunch::PunchFinish; sic.FinishPunch.Code = unit;
} }
} }
else{ else {
if (Mode==0x02 || Mode == 50){ if (Mode == 0x02 || Mode == 50) {
sic.Punch[0].Code=Station; sic.Punch[0].Code = Station;
sic.Punch[0].Time=Time; sic.Punch[0].Time = Time;
sic.nPunch=1; sic.nPunch = 1;
} }
else if (Mode == 3) { else if (Mode == 3) {
sic.StartPunch.Time=Time; sic.StartPunch.Time = Time;
sic.StartPunch.Code = oPunch::PunchStart; sic.StartPunch.Code = Station;
} }
else if (Mode == 10) { else if (Mode == 10) {
sic.CheckPunch.Time=Time; sic.CheckPunch.Time = Time;
sic.CheckPunch.Code = oPunch::PunchCheck; sic.CheckPunch.Code = Station;
} }
else{ else {
sic.FinishPunch.Time=Time; sic.FinishPunch.Time = Time;
sic.FinishPunch.Code = oPunch::PunchFinish; sic.FinishPunch.Code = Station;
} }
} }
sic.punchOnly = true; sic.punchOnly = true;
@ -2084,9 +2142,8 @@ void start_si_thread(void *ptr)
} }
} }
void SportIdent::startMonitorThread(const wchar_t *com) void SportIdent::startMonitorThread(const wchar_t *com) {
{ SI_StationInfo *si = findStationInt(com);
SI_StationInfo *si = findStation(com);
if (si && (si->hComm || si->ComPort==L"TCP" || si->ComPort == L"TEST")) if (si && (si->hComm || si->ComPort==L"TCP" || si->ComPort == L"TEST"))
{ {
@ -2119,7 +2176,7 @@ void checkport_si_thread(void *ptr)
*port=0; //No SI found here *port=0; //No SI found here
else { else {
bool valid = true; bool valid = true;
SI_StationInfo *sii = si.findStation(bf); const SI_StationInfo *sii = ((const SportIdent &)si).findStation(bf);
if (sii) { if (sii) {
if (sii->data.empty() || sii->data[0].stationNumber>=1024 || sii->data[0].stationMode>15 || if (sii->data.empty() || sii->data[0].stationNumber>=1024 || sii->data[0].stationMode>15 ||
!(sii->data[0].autoSend || sii->data[0].handShake)) !(sii->data[0].autoSend || sii->data[0].handShake))
@ -2178,7 +2235,7 @@ bool SportIdent::autoDetect(list<int> &ComPorts)
bool SportIdent::isPortOpen(const wstring &com) bool SportIdent::isPortOpen(const wstring &com)
{ {
SI_StationInfo *si = findStation(com); const SI_StationInfo *si = findStation(com);
if (si && si->ComPort==L"TCP") if (si && si->ComPort==L"TCP")
return tcpPortOpen && serverSocket; return tcpPortOpen && serverSocket;
@ -2186,25 +2243,61 @@ bool SportIdent::isPortOpen(const wstring &com)
return si!=0 && si->hComm && si->ThreadHandle; return si!=0 && si->hComm && si->ThreadHandle;
} }
void SportIdent::getInfoString(const wstring &com, vector<wstring> &infov)
{ bool SportIdent::isAnyOpenUnkownUnit() const {
if (tcpPortOpen && serverSocket)
return true;
for (int i = 0; i < n_SI_Info; i++) {
auto& si = SI_Info[i];
auto& com = si.ComPort;
if (com == L"TCP")
continue;
if (com == L"TEST")
return true;
if (!(si.hComm && si.ThreadHandle))
continue; // Not open
if (si.data.empty())
return true; // Listen mode (no contact made)
switch (si.data[0].stationMode) {
case 2: // Known modes
case 50:
case 4:
case 3:
case 5:
case 7:
case 10:
continue;
}
return true;
}
return false;
}
void SportIdent::getInfoString(const wstring &com, vector<pair<bool, wstring>> &infov) const {
infov.clear(); infov.clear();
SI_StationInfo *si = findStation(com); const SI_StationInfo *si = findStation(com);
if (com==L"TCP") { if (com==L"TCP") {
if (!si || !tcpPortOpen || !serverSocket) { if (!si || !tcpPortOpen || !serverSocket) {
infov.push_back(L"TCP: "+lang.tl(L"ej aktiv.")); infov.emplace_back(false, L"TCP: "+lang.tl(L"ej aktiv."));
return; return;
} }
wchar_t bf[128]; wchar_t bf[128];
swprintf_s(bf, lang.tl(L"TCP: Port %d, Nolltid: %s").c_str(), tcpPortOpen, L"00:00:00");//WCS swprintf_s(bf, lang.tl(L"TCP: Port %d, Nolltid: %s").c_str(), tcpPortOpen, L"00:00:00");//WCS
infov.push_back(bf); infov.emplace_back(false, bf);
return; return;
} }
if (!(si!=0 && si->hComm && si->ThreadHandle)) { if (!(si!=0 && si->hComm && si->ThreadHandle)) {
infov.push_back(com+L": "+lang.tl(L"ej aktiv.")); infov.emplace_back(false, com+L": "+lang.tl(L"ej aktiv."));
return; return;
} }
@ -2221,7 +2314,7 @@ void SportIdent::getInfoString(const wstring &com, vector<wstring> &infov)
switch(da.stationMode){ switch(da.stationMode){
case 2: case 2:
case 50: case 50:
info+=lang.tl(L"Kontrol"); info+=lang.tl(L"Kontroll");
break; break;
case 4: case 4:
info+=lang.tl(L"Mål"); info+=lang.tl(L"Mål");
@ -2256,12 +2349,15 @@ void SportIdent::getInfoString(const wstring &com, vector<wstring> &infov)
else if (da.handShake) info+=lang.tl(L"handskakning."); else if (da.handShake) info+=lang.tl(L"handskakning.");
else info+=lang.tl(L"[VARNING] ingen/okänd."); else info+=lang.tl(L"[VARNING] ingen/okänd.");
infov.push_back(info); infov.emplace_back(false, info);
if (da.autoSend && da.stationMode == 5)
infov.emplace_back(true, lang.tl("Programmera stationen utan AUTOSEND"));
} }
} }
static string formatTimeN(int t) { static string formatTimeN(int t) {
const wstring &wt = formatTime(t); const wstring &wt = formatTime(t, SubSecond::Auto);
string nt(wt.begin(), wt.end()); string nt(wt.begin(), wt.end());
return nt; return nt;
} }
@ -2411,7 +2507,7 @@ unsigned int SICard::calculateHash() const {
} }
string SICard::serializePunches() const { string SICard::serializePunches() const {
string ser; string ser = "*";// Mark of time factor
if (CheckPunch.Code != -1) if (CheckPunch.Code != -1)
ser += "C-" + itos(CheckPunch.Time); ser += "C-" + itos(CheckPunch.Time);
@ -2437,24 +2533,31 @@ void SICard::deserializePunches(const string &arg) {
StartPunch.Code = -1; StartPunch.Code = -1;
CheckPunch.Code = -1; CheckPunch.Code = -1;
vector<string> out; vector<string> out;
split(arg, ";", out); int timeFactor = 10;
if (arg.length() > 1 && arg[0] == '*') {
split(arg.c_str() + 2, ";", out);
timeFactor = 1;
}
else {
split(arg, ";", out);
}
nPunch = 0; nPunch = 0;
for (size_t k = 0; k< out.size(); k++) { for (size_t k = 0; k< out.size(); k++) {
vector<string> mark; vector<string> mark;
split(out[k], "-", mark); split(out[k], "-", mark);
if (mark.size() != 2) if (mark.size() != 2 || mark[0].empty())
throw std::exception("Invalid string"); throw std::exception("Invalid string");
DWORD *tp = 0; DWORD *tp = 0;
if (mark[0] == "F") { if (mark[0][0] == 'F') {
FinishPunch.Code = 1; FinishPunch.Code = atoi(mark[0].c_str() + 1);
tp = &FinishPunch.Time; tp = &FinishPunch.Time;
} }
else if (mark[0] == "S") { else if (mark[0][0] == 'S') {
StartPunch.Code = 1; StartPunch.Code = atoi(mark[0].c_str() + 1);
tp = &StartPunch.Time; tp = &StartPunch.Time;
} }
else if (mark[0] == "C") { else if (mark[0][0] == 'C') {
CheckPunch.Code = 1; CheckPunch.Code = atoi(mark[0].c_str() + 1);
tp = &CheckPunch.Time; tp = &CheckPunch.Time;
} }
else { else {
@ -2462,12 +2565,51 @@ void SICard::deserializePunches(const string &arg) {
tp = &Punch[nPunch++].Time; tp = &Punch[nPunch++].Time;
} }
*tp = atoi(mark[1].c_str()); *tp = atoi(mark[1].c_str()) * timeFactor;
} }
if (out.size() == 1) if (out.size() == 1)
punchOnly = true; punchOnly = true;
} }
int SICard::getFirstTime() const {
if (StartPunch.Time > 0)
return StartPunch.Time;
for (int i = 0; i < nPunch; i++) {
if (Punch[i].Time > 0)
return Punch[i].Time;
}
if (FinishPunch.Time > 0)
return FinishPunch.Time;
return 0;
}
map<int, oPunch::SpecialPunch> SportIdent::getSpecialMappings() const {
map<int, oPunch::SpecialPunch> res;
for (int j = 1; j < punchMap.size(); j++) {
if (punchMap[j] > 0)
res[j] = oPunch::SpecialPunch(punchMap[j]);
}
return res;
}
void SportIdent::addSpecialMapping(int code, oPunch::SpecialPunch p) {
if (code > 0 && code < punchMap.size())
punchMap[code] = p;
else
throw std::exception("Not supported");
}
void SportIdent::removeSpecialMapping(int code) {
if (code > 0 && code < punchMap.size())
punchMap[code] = 0;
else
throw std::exception("Not supported");
}
void SportIdent::addTestCard(int cardNo, const vector<int> &punches) { void SportIdent::addTestCard(int cardNo, const vector<int> &punches) {
testCards.emplace(cardNo, punches); testCards.emplace(cardNo, punches);
} }

View File

@ -1,19 +1,15 @@
// SportIdent.h: interface for the SportIdent class. // SportIdent.h: interface for the SportIdent class.
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#if !defined(AFX_SPORTIDENT_H__F13F5795_8FA9_4CE6_8497_7407CD590139__INCLUDED_)
#define AFX_SPORTIDENT_H__F13F5795_8FA9_4CE6_8497_7407CD590139__INCLUDED_
#if _MSC_VER > 1000
#pragma once #pragma once
#endif // _MSC_VER > 1000
#include <set> #include <set>
#include <vector>
#include "oPunch.h"
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -108,6 +104,8 @@ struct SICard
string serializePunches() const; string serializePunches() const;
void deserializePunches(const string &arg); void deserializePunches(const string &arg);
int getFirstTime() const;
}; };
struct SI_StationData { struct SI_StationData {
@ -154,9 +152,11 @@ struct SI_StationInfo
}; };
class SportIdent class SportIdent {
{
protected: protected:
bool useSubsecondMode = false;
bool readSI6Block(HANDLE hComm, BYTE *data); bool readSI6Block(HANDLE hComm, BYTE *data);
bool readSystemData(SI_StationInfo *si, int retry=2); bool readSystemData(SI_StationInfo *si, int retry=2);
bool readSystemDataV2(SI_StationInfo &si); bool readSystemDataV2(SI_StationInfo &si);
@ -195,7 +195,7 @@ protected:
void getSI9DataExt(HANDLE hComm); void getSI9DataExt(HANDLE hComm);
void analyseSI5Time(BYTE *data, DWORD &time, DWORD &control); void analyseSI5Time(BYTE *data, DWORD &time, DWORD &control);
bool analysePunch(BYTE *data, DWORD &time, DWORD &control); bool analysePunch(BYTE *data, DWORD &time, DWORD &control, bool subSecond);
void analyseTPunch(BYTE *data, DWORD &time, DWORD &control); void analyseTPunch(BYTE *data, DWORD &time, DWORD &control);
//Card read waiting to be processed. //Card read waiting to be processed.
@ -227,13 +227,25 @@ protected:
bool readVoltage; bool readVoltage;
vector<int> punchMap;
SI_StationInfo* findStationInt(const wstring& com);
void addTestStation(const wstring& com);
public: public:
SI_StationInfo *findStation(const wstring &com);
map<int, oPunch::SpecialPunch> getSpecialMappings() const;
void addSpecialMapping(int code, oPunch::SpecialPunch);
void removeSpecialMapping(int code);
const SI_StationInfo *findStation(const wstring &com) const;
/** Log debug data. */ /** Log debug data. */
void debugLog(const wchar_t *ptr); void debugLog(const wchar_t *ptr);
void getInfoString(const wstring &com, vector<wstring> &info); void getInfoString(const wstring &com, vector<pair<bool, wstring>> &info) const;
bool isAnyOpenUnkownUnit() const;
bool isPortOpen(const wstring &com); bool isPortOpen(const wstring &com);
bool autoDetect(list<int> &ComPorts); bool autoDetect(list<int> &ComPorts);
void stopMonitorThread(); void stopMonitorThread();
@ -250,13 +262,14 @@ public:
void closeCom(const wchar_t *com); void closeCom(const wchar_t *com);
bool openCom(const wchar_t *com); bool openCom(const wchar_t *com);
bool tcpAddPort(int port, DWORD zeroTime); bool tcpAddPort(int port, DWORD zeroTime);
bool openComListen(const wchar_t *com, DWORD BaudRate); bool openComListen(const wchar_t *com, DWORD BaudRate);
SportIdent(HWND hWnd, DWORD Id, bool readVoltage); SportIdent(HWND hWnd, DWORD Id, bool readVoltage);
void setSubSecondMode(bool subSec) { useSubsecondMode = subSec; }
void resetPunchMap();
virtual ~SportIdent(); virtual ~SportIdent();
friend void start_si_thread(void *ptr); friend void start_si_thread(void *ptr);
}; };
#endif // !defined(AFX_SPORTIDENT_H__F13F5795_8FA9_4CE6_8497_7407CD590139__INCLUDED_)

View File

@ -1,21 +1,12 @@
// stdafx.h : include file for standard system include files, #pragma once
// or project specific include files that are used frequently, but
// are changed infrequently
//
#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define NOMINMAX #define NOMINMAX
// Windows Header Files:
#include <windows.h> #include <windows.h>
#include <commctrl.h> #include <commctrl.h>
#include "timeconstants.hpp"
// C RunTime Header Files // C RunTime Header Files
@ -51,4 +42,3 @@ const extern string _EmptyString;
const extern string _VacantName; const extern string _VacantName;
const extern wstring _EmptyWString; const extern wstring _EmptyWString;
#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -716,7 +716,7 @@ void AutoMachine::startCancelInterval(gdioutput &gdi, const char *startCommand,
void PrintResultMachine::settings(gdioutput &gdi, oEvent &oe, State state) { void PrintResultMachine::settings(gdioutput &gdi, oEvent &oe, State state) {
settingsTitle(gdi, "Resultatutskrift / export"); settingsTitle(gdi, "Resultatutskrift / export");
wstring time = (state == State::Create && interval <= 0) ? L"10:00" : getTimeMS(interval); wstring time = (state == State::Create && interval <= 0) ? L"10:00" : formatTimeMS(interval, false, SubSecond::Off);
startCancelInterval(gdi, "Save", state, IntervalMinute, time); startCancelInterval(gdi, "Save", state, IntervalMinute, time);
if (state == State::Create) { if (state == State::Create) {
@ -822,12 +822,12 @@ void PrintResultMachine::settings(gdioutput &gdi, oEvent &oe, State state) {
} }
} }
void PrintResultMachine::save(oEvent &oe, gdioutput &gdi, bool doProcess) { void PrintResultMachine::save(oEvent& oe, gdioutput& gdi, bool doProcess) {
AutoMachine::save(oe, gdi, doProcess); AutoMachine::save(oe, gdi, doProcess);
wstring minute = gdi.getText("Interval"); wstring minute = gdi.getText("Interval");
int t = convertAbsoluteTimeMS(minute); int t = convertAbsoluteTimeMS(minute) / timeConstSecond;
if (t < 2 || t>7200) { if (t < 2 || t > 7200) {
throw meosException("Intervallet måste anges på formen MM:SS."); throw meosException("Intervallet måste anges på formen MM:SS.");
} }
doExport = gdi.isChecked("DoExport"); doExport = gdi.isChecked("DoExport");
@ -980,7 +980,7 @@ void PrewarningMachine::settings(gdioutput &gdi, oEvent &oe, State state) {
gdi.pushX(); gdi.pushX();
gdi.fillDown(); gdi.fillDown();
vector< pair<wstring, size_t> > d; vector< pair<wstring, size_t> > d;
oe.fillControls(d, oEvent::CTCourseControl); oe.fillControls(d, oEvent::ControlType::CourseControl);
gdi.addItem("Controls", d); gdi.addItem("Controls", d);
gdi.setSelection("Controls", controls); gdi.setSelection("Controls", controls);
gdi.popX(); gdi.popX();
@ -998,7 +998,7 @@ void PrewarningMachine::save(oEvent &oe, gdioutput &gdi, bool doProcess) {
controlsSI.clear(); controlsSI.clear();
for (set<int>::iterator it = controls.begin(); it != controls.end(); ++it) { for (set<int>::iterator it = controls.begin(); it != controls.end(); ++it) {
pControl pc = oe.getControl(*it, false); pControl pc = oe.getControl(*it, false, false);
if (pc) { if (pc) {
vector<int> n; vector<int> n;
pc->getNumbers(n); pc->getNumbers(n);
@ -1152,7 +1152,7 @@ void PunchMachine::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast)
sic.punchOnly = true; sic.punchOnly = true;
sic.nPunch = 1; sic.nPunch = 1;
sic.Punch[0].Code = radio; sic.Punch[0].Code = radio;
sic.Punch[0].Time = 600 + rand() % 1200 + r->getStartTime(); sic.Punch[0].Time = timeConstHour/10 + rand() % (1200*timeConstSecond) + r->getStartTime();
si.addCard(sic); si.addCard(sic);
} }
} }
@ -1205,7 +1205,7 @@ void SplitsMachine::save(oEvent &oe, gdioutput &gdi, bool doProcess) {
if (doProcess) { if (doProcess) {
//Try exporting. //Try exporting.
oe.exportIOFSplits(oEvent::IOF20, file.c_str(), true, false, oe.exportIOFSplits(oEvent::IOF20, file.c_str(), true, false,
set<int>(), -1, false, true, true, false); set<int>(), -1, false, true, true, false, false);
interval = iv; interval = iv;
synchronize = true; synchronize = true;
} }
@ -1245,7 +1245,7 @@ void SplitsMachine::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast)
if ((interval>0 && ast==SyncTimer) || (interval==0 && ast==SyncDataUp)) { if ((interval>0 && ast==SyncTimer) || (interval==0 && ast==SyncDataUp)) {
if (!file.empty()) if (!file.empty())
oe->exportIOFSplits(oEvent::IOF20, file.c_str(), true, false, classes, oe->exportIOFSplits(oEvent::IOF20, file.c_str(), true, false, classes,
leg, false, true, true, false); leg, false, true, true, false, false);
} }
} }
@ -1276,16 +1276,16 @@ void SaveMachine::status(gdioutput &gdi) {
void SaveMachine::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) { void SaveMachine::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) {
if (interval>0 && ast==SyncTimer) { if (interval>0 && ast==SyncTimer) {
if (!baseFile.empty()) { if (!baseFile.empty()) {
wstring file = baseFile + L"meos_backup_" + oe->getDate() + L"_" + itow(saveIter++) + L".xml"; wstring file = baseFile + L"meos_backup_" + oe->getDate() + L"_" + itow(saveIter++) + L".meosxml";
oe->autoSynchronizeLists(true); oe->autoSynchronizeLists(true);
oe->save(file); oe->save(file, false);
} }
} }
} }
void SaveMachine::settings(gdioutput &gdi, oEvent &oe, State state) { void SaveMachine::settings(gdioutput &gdi, oEvent &oe, State state) {
settingsTitle(gdi, "Säkerhetskopiering"); settingsTitle(gdi, "Säkerhetskopiering");
wstring time=state == State::Create ? L"10:00" : getTimeMS(interval); wstring time=state == State::Create ? L"10:00" : formatTimeMS(interval, false, SubSecond::Off);
startCancelInterval(gdi, "Save", state, IntervalMinute, time); startCancelInterval(gdi, "Save", state, IntervalMinute, time);
int cx = gdi.getCX(); int cx = gdi.getCX();
@ -1295,12 +1295,12 @@ void SaveMachine::settings(gdioutput &gdi, oEvent &oe, State state) {
gdi.setCX(cx); gdi.setCX(cx);
} }
void SaveMachine::save(oEvent &oe, gdioutput &gdi, bool doProcess) { void SaveMachine::save(oEvent& oe, gdioutput& gdi, bool doProcess) {
AutoMachine::save(oe, gdi, doProcess); AutoMachine::save(oe, gdi, doProcess);
wstring minute=gdi.getText("Interval"); wstring minute = gdi.getText("Interval");
int t=convertAbsoluteTimeMS(minute); int t = convertAbsoluteTimeMS(minute) / timeConstSecond;
if (t<2 || t>7200) { if (t < 2 || t>7200) {
throw meosException("Intervallet måste anges på formen MM:SS."); throw meosException("Intervallet måste anges på formen MM:SS.");
} }
wstring f = gdi.getText("BaseFile"); wstring f = gdi.getText("BaseFile");

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -76,10 +76,10 @@ TabClass::TabClass(oEvent *poe):TabBase(poe)
void TabClass::clearCompetitionData() { void TabClass::clearCompetitionData() {
currentResultModuleTags.clear(); currentResultModuleTags.clear();
pSettings.clear(); pSettings.clear();
pSavedDepth = 3600; pSavedDepth = timeConstHour;
pFirstRestart = 3600; pFirstRestart = timeConstHour;
pTimeScaling = 1.0; pTimeScaling = 1.0;
pInterval = 120; pInterval = 2 * timeConstMinute;
currentStage = -1; currentStage = -1;
EditChanged = false; EditChanged = false;
@ -241,6 +241,9 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
return true; return true;
} }
else if (bi.id == "ApplyForking") { else if (bi.id == "ApplyForking") {
int maxForking = gdi.getTextNo("MaxForkings");
if (maxForking < 2)
throw meosException("Du måste ange minst två gafflingsvarienater");
showForkingGuide = false; showForkingGuide = false;
pClass pc = oe->getClass(ClassId); pClass pc = oe->getClass(ClassId);
@ -259,7 +262,7 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
allR[k]->setCourseId(0); allR[k]->setCourseId(0);
} }
} }
pair<int,int> res = pc->autoForking(forkingSetup); pair<int,int> res = pc->autoForking(forkingSetup, maxForking);
gdi.alert("Created X distinct forkings using Y courses.#" + gdi.alert("Created X distinct forkings using Y courses.#" +
itos(res.first) + "#" + itos(res.second)); itos(res.first) + "#" + itos(res.second));
loadPage(gdi); loadPage(gdi);
@ -307,6 +310,10 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
gdi.setSelection("AllStages", set<int>()); gdi.setSelection("AllStages", set<int>());
gdi.disableInput("AssignCourses"); gdi.disableInput("AssignCourses");
} }
else if (bi.id == "AllCourses") {
gdi.setSelection("AllCourses", { -1 });
//gdi.enableInput("AssignCourses");
}
else if (bi.id == "ShowForking") { else if (bi.id == "ShowForking") {
if (!checkClassSelected(gdi)) if (!checkClassSelected(gdi))
return false; return false;
@ -469,7 +476,7 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
int nst = oe->convertAbsoluteTime(st); int nst = oe->convertAbsoluteTime(st);
if (nst >= 0 && warnDrawStartTime(gdi, nst, true)) { if (nst >= 0 && warnDrawStartTime(gdi, nst, true)) {
nst = 3600; nst = timeConstHour;
st = oe->getAbsTime(nst); st = oe->getAbsTime(nst);
} }
if (nst>0) if (nst>0)
@ -695,8 +702,27 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
EditChanged=true; EditChanged=true;
if (ii.id=="NStage") if (ii.id=="NStage")
gdi.enableInput("SetNStage"); gdi.enableInput("SetNStage");
else if (ii.id == "CourseFilter") {
gdi.addTimeoutMilli(500, "FilterCourseTimer", MultiCB);
}
//else if (ii.id=="") //else if (ii.id=="")
} }
else if (type == GUI_TIMER) {
TimerInfo& ti = *(TimerInfo*)(data);
if (ti.id == "FilterCourseTimer") {
const wstring &filter = gdi.getText("CourseFilter");
if (filter != courseFilter) {
courseFilter = filter;
vector<pair<wstring, size_t>> out;
oe->getCourses(out, courseFilter, true, false);
set<int> sel;
gdi.getSelection("AllCourses", sel);
gdi.addItem("AllCourses", out);
gdi.setSelection("AllCourses", sel);
}
}
}
return 0; return 0;
} }
@ -832,8 +858,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
else ds.ctrl = 0; else ds.ctrl = 0;
// Save settings with class // Save settings with class
ds.firstStart = 3600; ds.firstStart = timeConstHour;
ds.interval = 120; ds.interval = 2 * timeConstMinute;
ds.vacant = 1; ds.vacant = 1;
res.push_back(ds); res.push_back(ds);
@ -1023,7 +1049,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
gdi.pushX(); gdi.pushX();
gdi.fillRight(); gdi.fillRight();
gdi.addInput("FirstStart", oe->getAbsTime(3600), 10, 0, L"Första (ordinarie) start:"); gdi.addInput("FirstStart", oe->getAbsTime(timeConstHour), 10, 0, L"Första (ordinarie) start:");
gdi.addInput("MinInterval", L"2:00", 10, 0, L"Minsta startintervall:"); gdi.addInput("MinInterval", L"2:00", 10, 0, L"Minsta startintervall:");
gdi.addInput("Vacances", getDefaultVacant(), 10, 0, L"Andel vakanser:"); gdi.addInput("Vacances", getDefaultVacant(), 10, 0, L"Andel vakanser:");
gdi.fillDown(); gdi.fillDown();
@ -1085,7 +1111,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
} }
else if (bi.id == "SelectAllNoneP") { else if (bi.id == "SelectAllNoneP") {
bool select = bi.getExtraInt() != 0; bool select = bi.getExtraInt() != 0;
for (int k = 0; k < oe->getNumClasses(); k++) { const int nc = oe->getNumClasses();
for (int k = 0; k < nc; k++) {
gdi.check("PLUse" + itos(k), select); gdi.check("PLUse" + itos(k), select);
gdi.setInputStatus("First" + itos(k), select); gdi.setInputStatus("First" + itos(k), select);
} }
@ -1107,8 +1134,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
pInterval = interval; pInterval = interval;
oListParam par; oListParam par;
const int nc = oe->getNumClasses();
for (int k = 0; k < oe->getNumClasses(); k++) { for (int k = 0; k < nc; k++) {
if (!gdi.hasWidget("PLUse" + itos(k))) if (!gdi.hasWidget("PLUse" + itos(k)))
continue; continue;
BaseInfo *biu = gdi.setText("PLUse" + itos(k), L"", false); BaseInfo *biu = gdi.setText("PLUse" + itos(k), L"", false);
@ -1260,7 +1287,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
} }
else if (bi.id == "DrawAll") { else if (bi.id == "DrawAll") {
int origin = bi.getExtraInt(); int origin = bi.getExtraInt();
wstring firstStart = oe->getAbsTime(3600); wstring firstStart = oe->getAbsTime(timeConstHour);
wstring minInterval = L"2:00"; wstring minInterval = L"2:00";
wstring vacances = getDefaultVacant(); wstring vacances = getDefaultVacant();
if (gdi.hasWidget("Vacances")) { if (gdi.hasWidget("Vacances")) {
@ -2529,7 +2556,7 @@ void TabClass::showClassSettings(gdioutput &gdi)
if (ci.hasFixedTime) { if (ci.hasFixedTime) {
ii->setBgColor(fixedColor).setExtra(fixedColor); ii->setBgColor(fixedColor).setExtra(fixedColor);
} }
ii = &gdi.addInput(xp + classW + width, y, "I" + itos(id), formatTime(ci.interval*drawInfo.baseInterval), 7, DrawClassesCB); ii = &gdi.addInput(xp + classW + width, y, "I" + itos(id), formatTime(ci.interval*drawInfo.baseInterval, SubSecond::Auto), 7, DrawClassesCB);
if (ci.hasFixedTime) { if (ci.hasFixedTime) {
ii->setBgColor(fixedColor).setExtra(fixedColor); ii->setBgColor(fixedColor).setExtra(fixedColor);
} }
@ -3323,9 +3350,24 @@ bool TabClass::loadPage(gdioutput &gdi)
gdi.fillDown(); gdi.fillDown();
gdi.addListBox("Classes", 200, showAdvanced ? 512 : 420, ClassesCB, L"").isEdit(false).ignore(true); gdi.addListBox("Classes", 200, showAdvanced ? 512 : 420, ClassesCB, L"").isEdit(false).ignore(true);
gdi.setTabStops("Classes", 185); gdi.setTabStops("Classes", 170);
oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone); oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone);
bool hasIgnoreStart = false;
bool hasFreeStart = false;
if (!showAdvanced) {
vector<pClass> clsList;
oe->getClasses(clsList, false);
for (auto c : clsList) {
if (c->ignoreStartPunch())
hasIgnoreStart = true;
if (c->hasFreeStart())
hasFreeStart = true;
}
}
gdi.newColumn(); gdi.newColumn();
gdi.dropLine(2); gdi.dropLine(2);
@ -3419,11 +3461,15 @@ bool TabClass::loadPage(gdioutput &gdi)
gdi.addCheckbox("NoTiming", "Utan tidtagning", 0); gdi.addCheckbox("NoTiming", "Utan tidtagning", 0);
} }
if (showAdvanced) { if (showAdvanced || hasIgnoreStart || hasFreeStart) {
gdi.dropLine(2); gdi.dropLine(2);
gdi.popX(); gdi.popX();
gdi.addCheckbox("FreeStart", "Fri starttid", 0, false, "Klassen lottas inte, startstämpling");
gdi.addCheckbox("IgnoreStart", "Ignorera startstämpling", 0, false, "Uppdatera inte starttiden vid startstämpling"); if (showAdvanced || hasFreeStart)
gdi.addCheckbox("FreeStart", "Fri starttid", 0, false, "Klassen lottas inte, startstämpling");
if (showAdvanced || hasIgnoreStart)
gdi.addCheckbox("IgnoreStart", "Ignorera startstämpling", 0, false, "Uppdatera inte starttiden vid startstämpling");
gdi.dropLine(2); gdi.dropLine(2);
gdi.popX(); gdi.popX();
} }
@ -3721,8 +3767,8 @@ void TabClass::drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClas
return; return;
} }
int firstStart = 3600, int firstStart = timeConstHour,
interval = 120, interval = 2 * timeConstMinute,
vac = _wtoi(lastNumVac.c_str()); vac = _wtoi(lastNumVac.c_str());
int pairSize = lastPairSize; int pairSize = lastPairSize;
@ -3787,12 +3833,12 @@ void TabClass::drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClas
if (method == oEvent::DrawMethod::Pursuit || method == oEvent::DrawMethod::ReversePursuit) { if (method == oEvent::DrawMethod::Pursuit || method == oEvent::DrawMethod::ReversePursuit) {
gdi.addInput("MaxAfter", lastMaxAfter, 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart").setSynchData(&lastMaxAfter); gdi.addInput("MaxAfter", lastMaxAfter, 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart").setSynchData(&lastMaxAfter);
gdi.addInput("TimeRestart", oe->getAbsTime(firstStart + 3600), 8, 0, L"Första omstartstid:"); gdi.addInput("TimeRestart", oe->getAbsTime(firstStart + timeConstHour), 8, 0, L"Första omstartstid:");
gdi.addInput("ScaleFactor", lastScaleFactor, 8, 0, L"Tidsskalning:").setSynchData(&lastScaleFactor); gdi.addInput("ScaleFactor", lastScaleFactor, 8, 0, L"Tidsskalning:").setSynchData(&lastScaleFactor);
} }
if (method != oEvent::DrawMethod::Simultaneous) if (method != oEvent::DrawMethod::Simultaneous)
gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):").setSynchData(&lastInterval); gdi.addInput("Interval", formatTime(interval, SubSecond::Auto), 10, 0, L"Startintervall (min):").setSynchData(&lastInterval);
if ((method == oEvent::DrawMethod::Random || if ((method == oEvent::DrawMethod::Random ||
method == oEvent::DrawMethod::SOFT || method == oEvent::DrawMethod::SOFT ||
@ -3944,9 +3990,9 @@ void TabClass::pursuitDialog(gdioutput &gdi) {
gdi.fillRight(); gdi.fillRight();
gdi.addInput("MaxAfter", formatTime(pSavedDepth), 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart"); gdi.addInput("MaxAfter", formatTime(pSavedDepth, SubSecond::Off), 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart");
gdi.addInput("TimeRestart", L"+" + formatTime(pFirstRestart), 8, 0, L"Första omstartstid:", L"Ange tiden relativt klassens första start"); gdi.addInput("TimeRestart", L"+" + formatTime(pFirstRestart, SubSecond::Off), 8, 0, L"Första omstartstid:", L"Ange tiden relativt klassens första start");
gdi.addInput("Interval", formatTime(pInterval), 8, 0, L"Startintervall:", L"Ange startintervall för minutstart"); gdi.addInput("Interval", formatTime(pInterval, SubSecond::Off), 8, 0, L"Startintervall:", L"Ange startintervall för minutstart");
wchar_t bf[32]; wchar_t bf[32];
swprintf_s(bf, L"%f", pTimeScaling); swprintf_s(bf, L"%f", pTimeScaling);
gdi.addInput("ScaleFactor", bf, 8, 0, L"Tidsskalning:"); gdi.addInput("ScaleFactor", bf, 8, 0, L"Tidsskalning:");
@ -4027,7 +4073,7 @@ void TabClass::showClassSelection(gdioutput &gdi, int &bx, int &by, GUICALLBACK
int cx = gdi.getCX(); int cx = gdi.getCX();
int width = gdi.scaleLength(230); int width = gdi.scaleLength(230);
gdi.addListBox("Classes", 200, 480, classesCB, L"Klasser:", L"", true); gdi.addListBox("Classes", 200, 480, classesCB, L"Klasser:", L"", true);
gdi.setTabStops("Classes", 185); gdi.setTabStops("Classes", 170);
gdi.fillRight(); gdi.fillRight();
gdi.pushX(); gdi.pushX();
@ -4258,6 +4304,9 @@ void TabClass::defineForking(gdioutput &gdi, bool clearSettings) {
gdi.dropLine(2); gdi.dropLine(2);
gdi.pushY(); gdi.pushY();
courseFilter = L"";
gdi.addInput("CourseFilter", courseFilter, 16, MultiCB, L"Filtrera:");
gdi.addListBox("AllCourses", 180, 300, 0, L"Banor:", L"", true); gdi.addListBox("AllCourses", 180, 300, 0, L"Banor:", L"", true);
oe->fillCourses(gdi, "AllCourses", true); oe->fillCourses(gdi, "AllCourses", true);
int bxp = gdi.getCX(); int bxp = gdi.getCX();
@ -4292,6 +4341,9 @@ void TabClass::defineForking(gdioutput &gdi, bool clearSettings) {
} }
} }
gdi.dropLine();
gdi.addInput("MaxForkings", L"100", 5, nullptr, L"Max antal gaffllingsvarianter att skapa:",
L"Det uppskattade antalet startade lag i klassen är ett lämpligt värde.");
gdi.dropLine(); gdi.dropLine();
gdi.fillRight(); gdi.fillRight();
gdi.addButton("ApplyForking", "Calculate and apply forking", MultiCB); gdi.addButton("ApplyForking", "Calculate and apply forking", MultiCB);
@ -4300,9 +4352,11 @@ void TabClass::defineForking(gdioutput &gdi, bool clearSettings) {
gdi.setCX(bxp); gdi.setCX(bxp);
gdi.setCY(byp); gdi.setCY(byp);
gdi.addButton("AllCourses", "Välj allt", MultiCB);
gdi.fillDown(); gdi.fillDown();
gdi.addButton("ClearCourses", "Clear selections", MultiCB); gdi.addButton("ClearCourses", "Clear selections", MultiCB);
gdi.setCX(bxp);
gdi.addString("", 10, "help:assignforking"); gdi.addString("", 10, "help:assignforking");
gdi.addString("", ty, tx, boldLarge, L"Assign courses and apply forking to X#" + pc->getName()); gdi.addString("", ty, tx, boldLarge, L"Assign courses and apply forking to X#" + pc->getName());
@ -4384,7 +4438,7 @@ void TabClass::getClassSettingsTable(gdioutput &gdi, GUICALLBACK cb) {
gdi.addString("", yp, f, 1, "Direktanmälan"); gdi.addString("", yp, f, 1, "Direktanmälan");
vector< pair<wstring,size_t> > arg; vector< pair<wstring,size_t> > arg;
oe->fillCourses(arg, true); oe->getCourses(arg, L"", true);
for (size_t k = 0; k < cls.size(); k++) { for (size_t k = 0; k < cls.size(); k++) {
pClass it = cls[k]; pClass it = cls[k];
@ -4534,8 +4588,11 @@ void TabClass::updateStartData(gdioutput &gdi, pClass pc, int leg, bool updateDe
if (st == STChange) { if (st == STChange) {
if (typeid(sdataBase) != typeid(ListBoxInfo)) { if (typeid(sdataBase) != typeid(ListBoxInfo)) {
InputInfo sdII = dynamic_cast<InputInfo &>(sdataBase); InputInfo sdII = dynamic_cast<InputInfo &>(sdataBase);
string rp;
gdi.getWidgetRestorePoint(sdKey, rp);
gdi.removeWidget(sdKey); gdi.removeWidget(sdKey);
gdi.addSelection(sdII.getX(), sdII.getY(), sdKey, sdII.getWidth(), 200, MultiCB); gdi.addSelection(sdII.getX(), sdII.getY(), sdKey, int(sdII.getWidth()/gdi.getScale()), 200, MultiCB);
gdi.setWidgetRestorePoint(sdKey, rp);
setParallelOptions(sdKey, gdi, pc, leg); setParallelOptions(sdKey, gdi, pc, leg);
} }
else if (forceWrite) { else if (forceWrite) {
@ -4545,9 +4602,12 @@ void TabClass::updateStartData(gdioutput &gdi, pClass pc, int leg, bool updateDe
else { else {
if (typeid(sdataBase) != typeid(InputInfo)) { if (typeid(sdataBase) != typeid(InputInfo)) {
ListBoxInfo sdLBI = dynamic_cast<ListBoxInfo &>(sdataBase); ListBoxInfo sdLBI = dynamic_cast<ListBoxInfo &>(sdataBase);
string rp;
gdi.getWidgetRestorePoint(sdKey, rp);
gdi.removeWidget(sdKey); gdi.removeWidget(sdKey);
string val = "-"; string val = "-";
gdi.addInput(sdLBI.getX(), sdLBI.getY(), sdKey, pc->getStartDataS(leg), 8, MultiCB); gdi.addInput(sdLBI.getX(), sdLBI.getY(), sdKey, pc->getStartDataS(leg), 8, MultiCB);
gdi.setWidgetRestorePoint(sdKey, rp);
} }
else if (forceWrite) { else if (forceWrite) {
gdi.setText(sdKey, pc->getStartDataS(leg), true); gdi.setText(sdKey, pc->getStartDataS(leg), true);
@ -4678,12 +4738,12 @@ void TabClass::writeDrawInfo(gdioutput &gdi, const DrawInfo &drawInfoIn) {
gdi.setText("Vacances", itow(int(drawInfoIn.vacancyFactor *100.0)) + L"%"); gdi.setText("Vacances", itow(int(drawInfoIn.vacancyFactor *100.0)) + L"%");
gdi.setText("Extra", itow(int(drawInfoIn.extraFactor * 100.0) ) + L"%"); gdi.setText("Extra", itow(int(drawInfoIn.extraFactor * 100.0) ) + L"%");
gdi.setText("BaseInterval", formatTime(drawInfoIn.baseInterval)); gdi.setText("BaseInterval", formatTime(drawInfoIn.baseInterval, SubSecond::Off));
gdi.check("AllowNeighbours", drawInfoIn.allowNeighbourSameCourse); gdi.check("AllowNeighbours", drawInfoIn.allowNeighbourSameCourse);
gdi.check("CoursesTogether", drawInfoIn.coursesTogether); gdi.check("CoursesTogether", drawInfoIn.coursesTogether);
gdi.setText("MinInterval", formatTime(drawInfoIn.minClassInterval)); gdi.setText("MinInterval", formatTime(drawInfoIn.minClassInterval, SubSecond::Off));
gdi.setText("MaxInterval", formatTime(drawInfoIn.maxClassInterval)); gdi.setText("MaxInterval", formatTime(drawInfoIn.maxClassInterval, SubSecond::Off));
gdi.setText("nFields", drawInfoIn.nFields); gdi.setText("nFields", drawInfoIn.nFields);
gdi.setText("FirstStart", oe->getAbsTime(drawInfoIn.firstStart)); gdi.setText("FirstStart", oe->getAbsTime(drawInfoIn.firstStart));
} }
@ -4739,10 +4799,10 @@ bool TabClass::warnDrawStartTime(gdioutput &gdi, const wstring &firstStart) {
bool TabClass::warnDrawStartTime(gdioutput &gdi, int time, bool absTime) { bool TabClass::warnDrawStartTime(gdioutput &gdi, int time, bool absTime) {
if (absTime) if (absTime)
time = oe->getRelativeTime(formatTimeHMS(time)); time = oe->getRelativeTime(formatTimeHMS(time, SubSecond::Off));
if (!hasWarnedStartTime && (time > 3600 * 11 && !oe->useLongTimes())) { if (!hasWarnedStartTime && (time > timeConstHour * 11 && !oe->useLongTimes())) {
bool res = gdi.ask(L"warn:latestarttime#" + itow(time/3600)); bool res = gdi.ask(L"warn:latestarttime#" + itow(time/timeConstHour));
if (res) if (res)
hasWarnedStartTime = true; hasWarnedStartTime = true;
return !res; return !res;
@ -4812,7 +4872,7 @@ void DrawSettingsCSV::write(gdioutput &gdi, const oEvent &oe, const wstring &fn,
else line.emplace_back(""); else line.emplace_back("");
line.push_back(gdi.narrow(oe.getAbsTime(ci.firstStart))); line.push_back(gdi.narrow(oe.getAbsTime(ci.firstStart)));
line.push_back(gdi.narrow(formatTime(ci.interval))); line.push_back(gdi.narrow(formatTime(ci.interval, SubSecond::Off)));
line.push_back(itos(ci.vacant)); line.push_back(itos(ci.vacant));
writer.outputRow(line); writer.outputRow(line);
} }
@ -5130,8 +5190,8 @@ public:
else if (type == GuiEventType::GUI_BUTTON) { else if (type == GuiEventType::GUI_BUTTON) {
if (info.id == "AddGroup") { if (info.id == "AddGroup") {
int id = 1; int id = 1;
int firstStart = 3600; int firstStart = timeConstHour;
int length = 3600; int length = timeConstHour;
for (auto &g : oe.getStartGroups(false)) { for (auto &g : oe.getStartGroups(false)) {
id = max(id, g.first+1); id = max(id, g.first+1);
firstStart = max(firstStart, g.second.lastStart); firstStart = max(firstStart, g.second.lastStart);

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -33,9 +33,9 @@ class TabClass :
int maxTime; int maxTime;
PursuitSettings(oClass &c) { PursuitSettings(oClass &c) {
firstTime = 3600; firstTime = timeConstHour;
use = c.interpretClassType() != ctOpen; use = c.interpretClassType() != ctOpen;
maxTime = 3600; maxTime = timeConstHour;
} }
}; };
@ -103,6 +103,9 @@ class TabClass :
wstring lastScaleFactor; wstring lastScaleFactor;
wstring lastMaxAfter; wstring lastMaxAfter;
// Filter for course assignment
wstring courseFilter;
bool lastHandleBibs; bool lastHandleBibs;
// Generate a table with class settings // Generate a table with class settings
void showClassSettings(gdioutput &gdi); void showClassSettings(gdioutput &gdi);

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -351,8 +351,8 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data)
gdi.fillDown(); gdi.fillDown();
gdi.popX(); gdi.popX();
gdi.dropLine(2.5);
TabList::customTextLines(*oe, "IVExtra", gdi); TabList::customTextLines(*oe, "IVExtra", true, gdi);
gdi.dropLine(1); gdi.dropLine(1);
@ -364,7 +364,8 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data)
oe->getDI().fillDataFields(gdi); oe->getDI().fillDataFields(gdi);
} }
else if (bi.id == "SaveSettings") { else if (bi.id == "SaveSettings") {
oe->getDI().saveDataFields(gdi); set<string> modified;
oe->getDI().saveDataFields(gdi, modified);
TabList::saveExtraLines(*oe, "IVExtra", gdi); TabList::saveExtraLines(*oe, "IVExtra", gdi);

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -133,6 +133,8 @@ bool TabCompetition::save(gdioutput &gdi, bool write)
oe->updateStartTimes(delta); oe->updateStartTimes(delta);
} }
oe->supportSubSeconds(gdi.isChecked("SubSecond"));
oe->setDate(date, true); oe->setDate(date, true);
oe->useLongTimes(longTimes); oe->useLongTimes(longTimes);
oe->setName(gdi.getText("Name"), true); oe->setName(gdi.getText("Name"), true);
@ -163,7 +165,7 @@ bool TabCompetition::importFile(HWND hWnd, gdioutput &gdi)
return false; return false;
gdi.setWaitCursor(true); gdi.setWaitCursor(true);
if (oe->open(fileName, true, false)) { if (oe->open(fileName, true, false, false)) {
gdi.setWindowTitle(oe->getTitleName()); gdi.setWindowTitle(oe->getTitleName());
resetSaveTimer(); resetSaveTimer();
@ -171,7 +173,7 @@ bool TabCompetition::importFile(HWND hWnd, gdioutput &gdi)
wstring base = constructBase(L"base", L""); wstring base = constructBase(L"base", L"");
wchar_t newBase[_MAX_PATH]; wchar_t newBase[_MAX_PATH];
getUserFile(newBase, base.c_str()); getUserFile(newBase, base.c_str());
oe->save(newBase); oe->save(newBase, false);
return true; return true;
} }
@ -188,7 +190,7 @@ bool TabCompetition::exportFileAs(HWND hWnd, gdioutput &gdi)
return false; return false;
gdi.setWaitCursor(true); gdi.setWaitCursor(true);
if (!oe->save(fileName.c_str())) { if (!oe->save(fileName.c_str(), false)) {
gdi.alert(L"Fel: Filen " + fileName+ L" kunde inte skrivas."); gdi.alert(L"Fel: Filen " + fileName+ L" kunde inte skrivas.");
return false; return false;
} }
@ -662,7 +664,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.dropLine(3); gdi.dropLine(3);
gdi.popX(); gdi.popX();
gdi.addCheckbox("Clear", "Nollställ databaser", 0, true); gdi.addCheckbox("Clear", "Nollställ databaser");
gdi.addCheckbox("IncludeWithoutClub", "Inkludera klubblösa");
gdi.dropLine(3); gdi.dropLine(3);
gdi.popX(); gdi.popX();
@ -680,6 +684,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.setWaitCursor(true); gdi.setWaitCursor(true);
gdi.addString("", 0, "Importerar..."); gdi.addString("", 0, "Importerar...");
bool clear = gdi.isChecked("Clear"); bool clear = gdi.isChecked("Clear");
bool requireClub = !gdi.isChecked("IncludeWithoutClub");
wstring club = gdi.getText("ClubFile"); wstring club = gdi.getText("ClubFile");
wstring cmp = gdi.getText("CmpFile"); wstring cmp = gdi.getText("CmpFile");
if (club == cmp) if (club == cmp)
@ -698,7 +703,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
if (clubCsv) if (clubCsv)
throw meosException("Klubbfil får inte anges vid CSV import."); throw meosException("Klubbfil får inte anges vid CSV import.");
oe->importXML_IOF_Data(club, cmp, clear); oe->importXML_IOF_Data(club, cmp, requireClub, clear);
} }
gdi.dropLine(); gdi.dropLine();
@ -780,7 +785,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
wchar_t newBase[_MAX_PATH]; wchar_t newBase[_MAX_PATH];
getUserFile(newBase, base.c_str()); getUserFile(newBase, base.c_str());
if (!fileExists(newBase)) if (!fileExists(newBase))
oe->save(newBase); oe->save(newBase, false);
loadConnectionPage(gdi); loadConnectionPage(gdi);
} }
@ -911,7 +916,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
oEvent nextStage(gdi); oEvent nextStage(gdi);
if (!file.empty()) if (!file.empty())
success = nextStage.open(file.c_str(), false, false); success = nextStage.open(file.c_str(), false, false, false);
if (success) if (success)
success = nextStage.getNameId(0) == oe->getDCI().getString("PostEvent"); success = nextStage.getNameId(0) == oe->getDCI().getString("PostEvent");
@ -1138,7 +1143,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
wstring startlist = getTempFile(); wstring startlist = getTempFile();
bool eventorUTC = oe->getPropertyInt("UseEventorUTC", 0) != 0; bool eventorUTC = oe->getPropertyInt("UseEventorUTC", 0) != 0;
oe->exportIOFStartlist(oEvent::IOF30, startlist.c_str(), eventorUTC, oe->exportIOFStartlist(oEvent::IOF30, startlist.c_str(), eventorUTC,
set<int>(), false, false, true); set<int>(), false, false, true, true);
vector<wstring> fileList; vector<wstring> fileList;
fileList.push_back(startlist); fileList.push_back(startlist);
@ -1232,7 +1237,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
bool eventorUTC = oe->getPropertyInt("UseEventorUTC", 0) != 0; bool eventorUTC = oe->getPropertyInt("UseEventorUTC", 0) != 0;
oe->exportIOFSplits(oEvent::IOF30, resultlist.c_str(), false, oe->exportIOFSplits(oEvent::IOF30, resultlist.c_str(), false,
eventorUTC, classes, -1, false, true, eventorUTC, classes, -1, false, true,
false, true); false, true, true);
vector<wstring> fileList; vector<wstring> fileList;
fileList.push_back(resultlist); fileList.push_back(resultlist);
@ -1332,6 +1337,8 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.dropLine(3); gdi.dropLine(3);
gdi.popX(); gdi.popX();
gdi.addCheckbox("EventorDb", "Uppdatera löpardatabasen", CompetitionCB, true); gdi.addCheckbox("EventorDb", "Uppdatera löpardatabasen", CompetitionCB, true);
gdi.addCheckbox("IncludeWithoutClub", "Inkludera klubblösa");
gdi.dropLine(3); gdi.dropLine(3);
gdi.popX(); gdi.popX();
gdi.addButton("Cancel", "Avbryt", CompetitionCB); gdi.addButton("Cancel", "Avbryt", CompetitionCB);
@ -1339,14 +1346,16 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
} }
else if (bi.id == "EventorCmp") { else if (bi.id == "EventorCmp") {
gdi.setInputStatus("EventorSel", gdi.isChecked(bi.id)); gdi.setInputStatus("EventorSel", gdi.isChecked(bi.id));
gdi.setInputStatus("EventorNext", gdi.isChecked(bi.id) | gdi.isChecked("EventorDb")); gdi.setInputStatus("EventorNext", gdi.isChecked(bi.id) || gdi.isChecked("EventorDb"));
} }
else if (bi.id == "EventorDb") { else if (bi.id == "EventorDb") {
gdi.setInputStatus("EventorNext", gdi.isChecked(bi.id) | gdi.isChecked("EventorCmp")); gdi.setInputStatus("EventorNext", gdi.isChecked(bi.id) || gdi.isChecked("EventorCmp"));
} }
else if (bi.id == "EventorNext") { else if (bi.id == "EventorNext") {
bool cmp = gdi.isChecked("EventorCmp"); bool cmp = gdi.isChecked("EventorCmp");
bool db = gdi.isChecked("EventorDb"); bool db = gdi.isChecked("EventorDb");
bool withNoClub = gdi.isChecked("IncludeWithoutClub");
ListBoxInfo lbi; ListBoxInfo lbi;
gdi.getSelectedItem("EventorSel", lbi); gdi.getSelectedItem("EventorSel", lbi);
const CompetitionInfo *ci = 0; const CompetitionInfo *ci = 0;
@ -1355,6 +1364,8 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.clearPage(true); gdi.clearPage(true);
gdi.setData("UpdateDB", db); gdi.setData("UpdateDB", db);
gdi.setData("IncludeWithoutClub", withNoClub);
gdi.pushX(); gdi.pushX();
if (cmp && ci) { if (cmp && ci) {
gdi.setData("EventIndex", lbi.data); gdi.setData("EventIndex", lbi.data);
@ -1412,11 +1423,14 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.popX(); gdi.popX();
} }
else if (bi.id == "EventorImport") { else if (bi.id == "EventorImport") {
const int diffZeroTime = 3600; const int diffZeroTime = timeConstHour;
DWORD id; DWORD id;
DWORD db; DWORD db;
DWORD withNoClub;
gdi.getData("EventorId", id); gdi.getData("EventorId", id);
gdi.getData("UpdateDB", db); gdi.getData("UpdateDB", db);
gdi.getData("IncludeWithoutClub", withNoClub);
DWORD eventIndex; DWORD eventIndex;
gdi.getData("EventIndex", eventIndex); gdi.getData("EventIndex", eventIndex);
@ -1444,7 +1458,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
firstStart = t; firstStart = t;
zeroTime = t - diffZeroTime; zeroTime = t - diffZeroTime;
if (zeroTime<0) if (zeroTime<0)
zeroTime += 3600*24; zeroTime += timeConstHour * 24;
startType = gdi.getSelectedItem("StartType").first; startType = gdi.getSelectedItem("StartType").first;
lastEntry = gdi.getText("LastEntryDate"); lastEntry = gdi.getText("LastEntryDate");
@ -1509,8 +1523,8 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
else else
tRunnerDB = extractedFiles[0]; tRunnerDB = extractedFiles[0];
} }
oe->importXML_IOF_Data(tClubs, tRunnerDB, true); oe->importXML_IOF_Data(tClubs, tRunnerDB, withNoClub == 0, true);
removeTempFile(tClubs); removeTempFile(tClubs);
if (id > 0) { if (id > 0) {
@ -1522,6 +1536,12 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
if (createNew && id>0) { if (createNew && id>0) {
gdi.addString("", 1, "Skapar ny tävling"); gdi.addString("", 1, "Skapar ny tävling");
oe->newCompetition(L"New"); oe->newCompetition(L"New");
oe->loadDefaults();
bool importHiredCard = true;
if (importHiredCard)
importDefaultHiredCards(gdi);
oe->importXML_EntryData(gdi, tEvent, false, false, noFilter, noType); oe->importXML_EntryData(gdi, tEvent, false, false, noFilter, noType);
oe->setZeroTime(formatTimeHMS(zeroTime), false); oe->setZeroTime(formatTimeHMS(zeroTime), false);
oe->getDI().setDate("OrdinaryEntry", lastEntry); oe->getDI().setDate("OrdinaryEntry", lastEntry);
@ -1546,7 +1566,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
if (!course.empty()) { if (!course.empty()) {
gdi.dropLine(); gdi.dropLine();
TabCourse::runCourseImport(gdi, course, oe, true); TabCourse::runCourseImport(gdi, course, oe, true, false);
} }
set<int> clsWithRef; set<int> clsWithRef;
@ -1563,7 +1583,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
switch (startType) { switch (startType) {
case SMCommon: case SMCommon:
oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"0", oe->automaticDrawAll(gdi, formatTimeHMS(firstStart, SubSecond::Off), L"0",
L"0", oEvent::VacantPosition::Mixed, L"0", oEvent::VacantPosition::Mixed,
false, false, oEvent::DrawMethod::Random, 1); false, false, oEvent::DrawMethod::Random, 1);
break; break;
@ -1589,7 +1609,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
} }
} }
if (!skip) if (!skip)
oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"2:00", oe->automaticDrawAll(gdi, formatTimeHMS(firstStart, SubSecond::Off), L"2:00",
L"2", oEvent::VacantPosition::Mixed, L"2", oEvent::VacantPosition::Mixed,
true, false, oEvent::DrawMethod::MeOS, 1); true, false, oEvent::DrawMethod::MeOS, 1);
break; break;
@ -1780,7 +1800,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
if (filterIndex == ImportFormats::IOF30 || filterIndex == ImportFormats::IOF203) { if (filterIndex == ImportFormats::IOF30 || filterIndex == ImportFormats::IOF203) {
bool useUTC = oe->getDCI().getInt("UTC") != 0; bool useUTC = oe->getDCI().getInt("UTC") != 0;
oe->exportIOFStartlist(filterIndex == ImportFormats::IOF30 ? oEvent::IOF30 : oEvent::IOF20, oe->exportIOFStartlist(filterIndex == ImportFormats::IOF30 ? oEvent::IOF30 : oEvent::IOF20,
save.c_str(), useUTC, allTransfer, individual, includeStage, false); save.c_str(), useUTC, allTransfer, individual, includeStage, false, false);
} }
else if (filterIndex == ImportFormats::OE) { else if (filterIndex == ImportFormats::OE) {
oe->exportOECSV(save.c_str(), cSVLanguageHeaderIndex, false); oe->exportOECSV(save.c_str(), cSVLanguageHeaderIndex, false);
@ -1838,7 +1858,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
if (!gdi.hasWidget("LegType")) { if (!gdi.hasWidget("LegType")) {
oe->exportIOFSplits(ver, save.c_str(), true, useUTC, oe->exportIOFSplits(ver, save.c_str(), true, useUTC,
allTransfer, -1, false, unroll, includeStage, false); allTransfer, -1, false, unroll, includeStage, false, false);
} }
else { else {
ListBoxInfo leglbi; ListBoxInfo leglbi;
@ -1860,17 +1880,17 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
for (int leg = 0; leg<legMax; leg++) { for (int leg = 0; leg<legMax; leg++) {
file = fileBase + L"_" + itow(leg+1) + fileEnd; file = fileBase + L"_" + itow(leg+1) + fileEnd;
oe->exportIOFSplits(ver, file.c_str(), true, useUTC, oe->exportIOFSplits(ver, file.c_str(), true, useUTC,
allTransfer, leg, false, unroll, includeStage, false); allTransfer, leg, false, unroll, includeStage, false, false);
} }
} }
else if (leglbi.data == 3) { else if (leglbi.data == 3) {
oe->exportIOFSplits(ver, file.c_str(), true, useUTC, allTransfer, oe->exportIOFSplits(ver, file.c_str(), true, useUTC, allTransfer,
-1, true, unroll, includeStage, false); -1, true, unroll, includeStage, false, false);
} }
else { else {
int leg = leglbi.data == 1 ? -1 : leglbi.data - 10; int leg = leglbi.data == 1 ? -1 : leglbi.data - 10;
oe->exportIOFSplits(ver, file.c_str(), true, useUTC, allTransfer, oe->exportIOFSplits(ver, file.c_str(), true, useUTC, allTransfer,
leg, false, unroll, includeStage, false); leg, false, unroll, includeStage, false, false);
} }
} }
} }
@ -1951,6 +1971,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
} }
oe->newCompetition(lang.tl(L"Ny tävling")); oe->newCompetition(lang.tl(L"Ny tävling"));
oe->loadDefaults();
gdi.setWindowTitle(L""); gdi.setWindowTitle(L"");
if (useEventor()) { if (useEventor()) {
@ -2077,15 +2098,18 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.disableInput("Cancel"); gdi.disableInput("Cancel");
gdi.disableInput("BrowseCourse"); gdi.disableInput("BrowseCourse");
gdi.disableInput("AddClasses"); gdi.disableInput("AddClasses");
gdi.disableInput("CreateClasses");
try { try {
TabCourse::runCourseImport(gdi, filename, oe, gdi.isChecked("AddClasses")); TabCourse::runCourseImport(gdi, filename, oe, gdi.isChecked("AddClasses"),
gdi.isChecked("CreateClasses"));
} }
catch (std::exception &) { catch (const std::exception &) {
gdi.enableInput("DoImportCourse"); gdi.enableInput("DoImportCourse");
gdi.enableInput("Cancel"); gdi.enableInput("Cancel");
gdi.enableInput("BrowseCourse"); gdi.enableInput("BrowseCourse");
gdi.enableInput("AddClasses"); gdi.enableInput("AddClasses");
gdi.enableInput("CreateClasses");
throw; throw;
} }
gdi.dropLine(); gdi.dropLine();
@ -2136,7 +2160,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.setWaitCursor(true); gdi.setWaitCursor(true);
xml.openOutput(fileName.c_str(), false); xml.openOutput(fileName.c_str(), false);
IOF30Interface writer(oe, false); IOF30Interface writer(oe, false, false);
writer.writeRunnerDB(oe->getRunnerDatabase(), xml); writer.writeRunnerDB(oe->getRunnerDatabase(), xml);
gdi.setWaitCursor(false); gdi.setWaitCursor(false);
} }
@ -2151,7 +2175,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.setWaitCursor(true); gdi.setWaitCursor(true);
xml.openOutput(fileName.c_str(), false); xml.openOutput(fileName.c_str(), false);
IOF30Interface writer(oe, false); IOF30Interface writer(oe, false, false);
writer.writeClubDB(oe->getRunnerDatabase(), xml); writer.writeClubDB(oe->getRunnerDatabase(), xml);
gdi.setWaitCursor(false); gdi.setWaitCursor(false);
} }
@ -2242,6 +2266,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.setInputStatus(gdi.narrow(fn).c_str(), !ii.text.empty()); gdi.setInputStatus(gdi.narrow(fn).c_str(), !ii.text.empty());
} }
} }
else if (ii.id == "Name") {
updateWarning(gdi);
}
} }
else if (type==GUI_EVENT) { else if (type==GUI_EVENT) {
EventInfo ei=*(EventInfo *)data; EventInfo ei=*(EventInfo *)data;
@ -2277,16 +2304,16 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.setData("RunnerIx", ix); gdi.setData("RunnerIx", ix);
gdi.dropLine(); gdi.dropLine();
gdi.addSelection("Classes", 200, 300, 0, L"Klasser:"); gdi.addSelection("Classes", 200, 300, 0, L"Klasser:");
oe->fillClasses(gdi, "Classes", oEvent::extraNone, oEvent::filterNone); oe->fillClasses(gdi, "Classes", oEvent::extraNone, oEvent::filterOnlySingle);
if (lastSelectedClass != -1) if (lastSelectedClass == -1 || !gdi.selectItemByData("Classes", lastSelectedClass))
gdi.selectItemByData("Classes", lastSelectedClass);
else
gdi.selectFirstItem("Classes"); gdi.selectFirstItem("Classes");
gdi.dropLine(); gdi.dropLine();
gdi.fillRight(); gdi.fillRight();
gdi.addButton("DBEntry", "Anmäl", CompetitionCB).setDefault(); gdi.addButton("DBEntry", "Anmäl", CompetitionCB).setDefault();
gdi.setInputStatus("DBEntry", gdi.getSelectedItem("Classes").first > 0); // Cannot change
gdi.addButton("CancelEntry", "Avbryt", CompetitionCB).setCancel(); gdi.addButton("CancelEntry", "Avbryt", CompetitionCB).setCancel();
gdi.refresh(); gdi.refresh();
} }
@ -2339,7 +2366,7 @@ int TabCompetition::restoreCB(gdioutput &gdi, int type, void *data) {
if (ti.id == "") { if (ti.id == "") {
wstring fi(bi.FullPath); wstring fi(bi.FullPath);
if (!oe->open(fi, false, false)) { if (!oe->open(fi, false, false, false)) {
gdi.alert("Kunde inte öppna tävlingen."); gdi.alert("Kunde inte öppna tävlingen.");
} }
else { else {
@ -2392,7 +2419,7 @@ void TabCompetition::copyrightLine(gdioutput &gdi) const
gdi.dropLine(0.4); gdi.dropLine(0.4);
gdi.fillDown(); gdi.fillDown();
gdi.addString("", 0, makeDash(L"#Copyright © 2007-2022 Melin Software HB")); gdi.addString("", 0, makeDash(L"#Copyright © 2007-2023 Melin Software HB"));
gdi.dropLine(1); gdi.dropLine(1);
gdi.popX(); gdi.popX();
@ -2422,7 +2449,7 @@ void TabCompetition::loadAboutPage(gdioutput &gdi) const
gdi.dropLine(1.5); gdi.dropLine(1.5);
gdi.setCX(gdi.getCX() + gdi.scaleLength(20)); gdi.setCX(gdi.getCX() + gdi.scaleLength(20));
gdi.addStringUT(1, makeDash(L"Copyright © 2007-2022 Melin Software HB")); gdi.addStringUT(1, makeDash(L"Copyright © 2007-2023 Melin Software HB"));
gdi.dropLine(); gdi.dropLine();
gdi.addStringUT(10, "The database connection used is MySQL++\nCopyright " gdi.addStringUT(10, "The database connection used is MySQL++\nCopyright "
"(c) 1998 by Kevin Atkinson, (c) 1999, 2000 and 2001 by MySQL AB," "(c) 1998 by Kevin Atkinson, (c) 1999, 2000 and 2001 by MySQL AB,"
@ -2463,6 +2490,20 @@ void TabCompetition::loadAboutPage(gdioutput &gdi) const
gdi.refresh(); gdi.refresh();
} }
void TabCompetition::updateWarning(gdioutput &gdi) const {
const wstring &n = gdi.getText("Name");
const wstring &w = gdi.getText("cmpwarning");
constexpr int limit = 32;
if (n.length() < limit && !w.empty()) {
gdi.setText("warningicon", L"", true);
gdi.setText("cmpwarning", L"", true);
}
else if (n.length() >= limit && w.empty()) {
gdi.setText("warningicon", L"514", true);
gdi.setText("cmpwarning", L"Ett långt tävlingsnamn kan ge oväntad nerskalning av utskrifter.", true);
}
}
bool TabCompetition::useEventor() const { bool TabCompetition::useEventor() const {
return oe->getPropertyInt("UseEventor", 0) == 1; return oe->getPropertyInt("UseEventor", 0) == 1;
} }
@ -2566,7 +2607,7 @@ bool TabCompetition::loadPage(gdioutput &gdi)
gdi.pushX(); gdi.pushX();
gdi.fillRight(); gdi.fillRight();
gdi.addInput("Name", oe->getName(), 24, 0, L"Tävlingsnamn:"); gdi.addInput("Name", oe->getName(), 24, CompetitionCB, L"Tävlingsnamn:");
gdi.fillDown(); gdi.fillDown();
gdi.addInput("Annotation", oe->getAnnotation(), 20, 0, L"Kommentar / version:") gdi.addInput("Annotation", oe->getAnnotation(), 20, 0, L"Kommentar / version:")
@ -2579,12 +2620,16 @@ bool TabCompetition::loadPage(gdioutput &gdi)
gdi.fillDown(); gdi.fillDown();
gdi.dropLine(1.2); gdi.dropLine(1.2);
//int ccx = gdi.getCX();
gdi.addCheckbox("LongTimes", "Aktivera stöd för tider över 24 timmar", CompetitionCB, oe->useLongTimes()); gdi.addCheckbox("LongTimes", "Aktivera stöd för tider över 24 timmar", CompetitionCB, oe->useLongTimes());
gdi.addCheckbox("SubSecond", "Aktivera stöd för tiondels sekunder", CompetitionCB, oe->supportSubSeconds());
gdi.dropLine(0.3);
if (false && oe->isClient()) { if (false && oe->isClient()) {
gdi.popX(); gdi.popX();
gdi.disableInput("ZeroTime"); gdi.disableInput("ZeroTime");
gdi.disableInput("LongTimes"); gdi.disableInput("LongTimes");
gdi.disableInput("SubSecond");
if (oe->useLongTimes()) if (oe->useLongTimes())
gdi.disableInput("Date"); gdi.disableInput("Date");
} }
@ -2651,6 +2696,14 @@ bool TabCompetition::loadPage(gdioutput &gdi)
gdi.fillDown(); gdi.fillDown();
gdi.popX(); gdi.popX();
gdi.fillRight();
gdi.addString("warningicon", textImage, "S25");
gdi.dropLine(0.2);
gdi.fillDown();
gdi.addString("cmpwarning", 0, "");
updateWarning(gdi);
gdi.popX();
gdi.newColumn(); gdi.newColumn();
gdi.dropLine(3); gdi.dropLine(3);
gdi.setCX(gdi.getCX()+gdi.scaleLength(60)); gdi.setCX(gdi.getCX()+gdi.scaleLength(60));
@ -2908,26 +2961,26 @@ void TabCompetition::getEventorCompetitions(gdioutput &gdi,
date.getObjectString("Clock", ci.firstStart); date.getObjectString("Clock", ci.firstStart);
if (useEventorUTC()) { if (useEventorUTC()) {
int offset = getTimeZoneInfo(ci.Date); int offset = getTimeZoneInfo(ci.Date) * timeConstSecond;
int t = convertAbsoluteTimeISO(ci.firstStart); int t = convertAbsoluteTimeISO(ci.firstStart);
int nt = t - offset; int nt = t - offset;
int dayOffset = 0; int dayOffset = 0;
if (nt < 0) { if (nt < 0) {
nt += 24*3600; nt += 24 * timeConstHour;
dayOffset = -1; dayOffset = -1;
} }
else if (nt > 24*3600) { else if (nt > 24* timeConstHour) {
nt -= 24*3600; nt -= 24* timeConstHour;
dayOffset = 1; dayOffset = 1;
} }
ci.firstStart = formatTimeHMS(nt); ci.firstStart = formatTimeHMS(nt, SubSecond::Off);
//TODO: Take dayoffset into account //TODO: Take dayoffset into account
} }
xmlEvents[k].getObjectString("WebURL", ci.url); xmlEvents[k].getObjectString("WebURL", ci.url);
xmlobject aco = xmlEvents[k].getObject("Account"); xmlobject aco = xmlEvents[k].getObject("Account");
if (aco) { if (aco) {
string type = aco.getAttrib("type").get(); string type = aco.getAttrib("type").getStr();
wstring no; wstring no;
aco.getObjectString("AccountNo", no); aco.getObjectString("AccountNo", no);
@ -2960,8 +3013,8 @@ void TabCompetition::getEventorCompetitions(gdioutput &gdi,
SYSTEMTIME st; SYSTEMTIME st;
convertDateYMS(breakDate, st, false); convertDateYMS(breakDate, st, false);
__int64 time = SystemTimeToInt64Second(st) - 1; __int64 time = SystemTimeToInt64TenthSecond(st) - 10;
breakDate = convertSystemDate(Int64SecondToSystemTime(time)); breakDate = convertSystemDate(Int64TenthSecondToSystemTime(time));
if (ci.lastNormalEntryDate.empty() || ci.lastNormalEntryDate >= breakDate) if (ci.lastNormalEntryDate.empty() || ci.lastNormalEntryDate >= breakDate)
ci.lastNormalEntryDate = breakDate; ci.lastNormalEntryDate = breakDate;
@ -3080,7 +3133,9 @@ void TabCompetition::getEventorCmpData(gdioutput &gdi, int id,
if (dbFile.length() > 0) { if (dbFile.length() > 0) {
gdi.addString("", 0, "Hämtar löpardatabasen..."); gdi.addString("", 0, "Hämtar löpardatabasen...");
gdi.refreshFast(); gdi.refreshFast();
dwl.downloadFile(eventorBase + L"export/cachedcompetitors?organisationIds=1&includePreselectedClasses=false&zip=true" + iofExportVersion, dbFile, key); //dwl.downloadFile(eventorBase + L"export/cachedcompetitors?organisationIds=1&includePreselectedClasses=false&zip=true" + iofExportVersion, dbFile, key);
dwl.downloadFile(eventorBase + L"export/cachedcompetitors?includePreselectedClasses=false&zip=true" + iofExportVersion, dbFile, key);
dwl.createDownloadThread(); dwl.createDownloadThread();
while (dwl.isWorking()) { while (dwl.isWorking()) {
Sleep(100); Sleep(100);
@ -3640,7 +3695,7 @@ FlowOperation TabCompetition::checkStageFilter(gdioutput & gdi,
xml.read(fname); xml.read(fname);
xmlobject xo = xml.getObject("EntryList"); xmlobject xo = xml.getObject("EntryList");
set<int> scanFilter; set<int> scanFilter;
IOF30Interface reader(oe, false); IOF30Interface reader(oe, false, false);
vector<string> idProviders; vector<string> idProviders;
if (xo) { if (xo) {
if (xo.getAttrib("iofVersion")) { if (xo.getAttrib("iofVersion")) {
@ -3967,9 +4022,7 @@ void TabCompetition::loadSettings(gdioutput &gdi) {
gdi.addString("", 1, "Tidszon"); gdi.addString("", 1, "Tidszon");
gdi.dropLine(0.3); gdi.dropLine(0.3);
gdi.addCheckbox("UTC", "Exportera tider i UTC", 0, gdi.addCheckbox("UTC", "Exportera tider i UTC", nullptr, oe->getDCI().getInt("UTC") != 0);
oe->getDCI().getInt("UTC") == 1);
gdi.newColumn(); gdi.newColumn();
gdi.popY(); gdi.popY();
@ -3999,16 +4052,60 @@ void TabCompetition::loadSettings(gdioutput &gdi) {
gdi.dropLine(3); gdi.dropLine(3);
gdi.addString("", 1, "Åldersgränser, reducerad anmälningsavgift"); gdi.addString("", 1, "Åldersgränser, reducerad anmälningsavgift");
fields.clear(); gdi.addString("", 0, "Ungdomar och äldre kan få reducerad avgift");
fields.push_back("YouthAge"); gdi.dropLine(0.5);
fields.push_back("SeniorAge");
gdi.fillRight(); gdi.fillRight();
oe->getDI().buildDataFields(gdi, fields, 10); auto yfields = oe->getDI().buildDataFields(gdi, { "YouthAge" , "SeniorAge" }, 10);
class HandleAge : public GuiHandler {
public:
void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) final {
if (type == GuiEventType::GUI_INPUT)
update(gdi, true);
}
void update(gdioutput &gdi, bool update) {
int youth = gdi.getTextNo("YouthAge_odc");
int senior = gdi.getTextNo("SeniorAge_odc");
InputInfo &ii = dynamic_cast<InputInfo &>(gdi.getBaseInfo("SeniorAge_odc"));
bool warn = senior > 1 && senior <= youth + 1;
ii.setBgColor(warn ? colorLightRed : colorDefault);
string i1, i2, i3;
if (youth <= 0 && senior <= 0) {
i1 = "Ingen reducerad avgift";
}
else {
i1 = "Reducerad avgift för:";
if (youth > 0)
i2 = "Unga, till och med X år#" + itos(youth);
else
i2 = "Äldre, från och med X år#" + itos(senior);
if (youth > 0 && senior > 0)
i3 = "Äldre, från och med X år#" + itos(senior);
}
gdi.setText("ReduceInfo", lang.tl(i1), update);
gdi.setText("ReduceInfoL1", i2.empty() ? L"" : L"\u25AA " + lang.tl(i2), update);
gdi.setText("ReduceInfoL2", i3.empty() ? L"" : L"\u25AA " + lang.tl(i3), update);
}
};
gdi.fillDown(); gdi.fillDown();
gdi.popX(); gdi.popX();
gdi.dropLine(3); gdi.dropLine(2.8);
gdi.addString("ReduceInfo", 0, "Ingen reducerad avgift");
gdi.addString("ReduceInfoL1", 0, "-");
gdi.addString("ReduceInfoL2", 0, "-");
auto handler = make_shared<HandleAge>();
yfields[0]->setHandler(handler);
yfields[1]->setHandler(handler);
gdi.dropLine(0.3);
gdi.addString("", 1, "Valuta"); gdi.addString("", 1, "Valuta");
fields.clear(); fields.clear();
@ -4053,6 +4150,8 @@ void TabCompetition::loadSettings(gdioutput &gdi) {
oe->getDI().buildDataFields(gdi, fields, 10); oe->getDI().buildDataFields(gdi, fields, 10);
oe->getDI().fillDataFields(gdi); oe->getDI().fillDataFields(gdi);
handler->update(gdi, false);
gdi.dropLine(1); gdi.dropLine(1);
int bottom = gdi.getCY(); int bottom = gdi.getCY();
@ -4097,17 +4196,16 @@ void TabCompetition::loadSettings(gdioutput &gdi) {
} }
void TabCompetition::saveSettings(gdioutput &gdi) { void TabCompetition::saveSettings(gdioutput &gdi) {
vector<string> fields; vector<string> fields = { "CardFee" ,"EliteFee" ,"EntryFee","YouthFee" };
vector<int> fees(4); vector<int> fees(4);
fields.push_back("CardFee");
fields.push_back("EliteFee");
fields.push_back("EntryFee");
fields.push_back("YouthFee");
for (int k = 0; k<4; k++) for (int k = 0; k<4; k++)
fees[k] = oe->getDCI().getInt(fields[k]); fees[k] = oe->getDCI().getInt(fields[k]);
wstring factor = oe->getDCI().getString("LateEntryFactor"); wstring factor = oe->getDCI().getString("LateEntryFactor");
oe->getDI().saveDataFields(gdi); set<string> modified;
oe->getDI().saveDataFields(gdi, modified);
bool changedFee = false; bool changedFee = false;
bool changedCardFee = false; bool changedCardFee = false;
@ -4119,33 +4217,60 @@ void TabCompetition::saveSettings(gdioutput &gdi) {
else { else {
changedCardFee = true; changedCardFee = true;
if (oe->getDCI().getInt(fields[k]) == 0) if (oe->getDCI().getInt(fields[k]) == 0)
oe->getDI().setInt(fields[k].c_str(), -1); // Disallow zero card fee. -1 means no fee. oe->getDI().setInt(fields[k], -1); // Disallow zero card fee. -1 means no fee.
} }
} }
} }
if (factor != oe->getDCI().getString("LateEntryFactor")) if (factor != oe->getDCI().getString("LateEntryFactor"))
changedFee = true; changedFee = true;
oe->getDI().setInt("UTC", gdi.isChecked("UTC") ? 1 : 0); if (oe->getDI().setInt("UTC", gdi.isChecked("UTC") ? 1 : 0))
setEventorUTC(gdi.isChecked("UTC"));
if (oe->getDI().setInt("CurrencyFactor", gdi.isChecked("UseFraction") ? 100 : 1))
modified.insert("CurrencyFactor");
if (oe->getDI().setInt("CurrencyPreSymbol", gdi.isChecked("PreSymbol") ? 1 : 0))
modified.insert("CurrencyPreSymbol");
oe->getDI().setInt("CurrencyFactor", gdi.isChecked("UseFraction") ? 100 : 1);
oe->getDI().setInt("CurrencyPreSymbol", gdi.isChecked("PreSymbol") ? 1 : 0);
oe->setCurrency(-1, L"", L"", false); oe->setCurrency(-1, L"", L"", false);
vector< pair<wstring, size_t> > modes; wstring pm = oe->getDCI().getString("PayModes");
vector<pair<wstring, size_t>> modes;
oe->getPayModes(modes); oe->getPayModes(modes);
for (size_t k = 0; k < modes.size(); k++) { for (size_t k = 0; k < modes.size(); k++) {
string field = "M"+itos(k); string field = "M"+itos(k);
if (gdi.hasWidget(field)) { if (gdi.hasWidget(field)) {
wstring mode = gdi.getText("M"+itos(k)); wstring mode = gdi.getText("M"+itos(k));
int id = gdi.getBaseInfo(field.c_str()).getExtraInt(); int id = gdi.getBaseInfo(field).getExtraInt();
oe->setPayMode(id, mode); oe->setPayMode(id, mode);
} }
} }
if (pm != oe->getDCI().getString("PayModes"))
modified.insert("PayModes");
// Read from model // Read from model
if (oe->isChanged()) { if (oe->isChanged()) {
oe->setProperty("Organizer", oe->getDCI().getString("Organizer")); vector<string> props = { "Organizer" ,"Street" ,"Address" ,"EMail" ,"Homepage" ,
"YouthAge","SeniorAge","Account", "LateEntryFactor","CurrencySymbol",
"CurrencyFactor","CurrencyPreSymbol","CurrencySeparator",
"CardFee","EliteFee","EntryFee","YouthFee", "PayModes" };
auto dci = oe->getDCI();
for (const string &p : props) {
if (modified.count(p)) {
if (dci.isString(p))
oe->setProperty(p.c_str(), dci.getString(p));
else if (dci.isInt(p))
oe->setProperty(p.c_str(), dci.getInt(p));
else
throw std::exception();
}
}
//oe->setProperty("PayModes", oe->getDCI().getString("PayModes"));
/*oe->setProperty("Organizer", oe->getDCI().getString("Organizer"));
oe->setProperty("Street", oe->getDCI().getString("Street")); oe->setProperty("Street", oe->getDCI().getString("Street"));
oe->setProperty("Address", oe->getDCI().getString("Address")); oe->setProperty("Address", oe->getDCI().getString("Address"));
oe->setProperty("EMail", oe->getDCI().getString("EMail")); oe->setProperty("EMail", oe->getDCI().getString("EMail"));
@ -4166,8 +4291,8 @@ void TabCompetition::saveSettings(gdioutput &gdi) {
oe->setProperty("CurrencyFactor", oe->getDCI().getInt("CurrencyFactor")); oe->setProperty("CurrencyFactor", oe->getDCI().getInt("CurrencyFactor"));
oe->setProperty("CurrencyPreSymbol", oe->getDCI().getInt("CurrencyPreSymbol")); oe->setProperty("CurrencyPreSymbol", oe->getDCI().getInt("CurrencyPreSymbol"));
oe->setProperty("CurrencySeparator", oe->getDCI().getString("CurrencySeparator")); oe->setProperty("CurrencySeparator", oe->getDCI().getString("CurrencySeparator"));
*/
oe->setProperty("PayModes", oe->getDCI().getString("PayModes")); //oe->setProperty("PayModes", oe->getDCI().getString("PayModes"));
} }
oe->synchronize(true); oe->synchronize(true);
set<int> dummy; set<int> dummy;
@ -4260,7 +4385,7 @@ void TabCompetition::mergeCompetition(gdioutput &gdi) {
if (!thisFile.empty()) { if (!thisFile.empty()) {
wchar_t newBase[_MAX_PATH]; wchar_t newBase[_MAX_PATH];
getUserFile(newBase, thisFile.c_str()); getUserFile(newBase, thisFile.c_str());
mergeEvent->save(newBase); mergeEvent->save(newBase, false);
} }
tc->oe->merge(*mergeEvent, baseEvent.get(), allowRemove, numAdd, numRemove, numUpdate); tc->oe->merge(*mergeEvent, baseEvent.get(), allowRemove, numAdd, numRemove, numUpdate);
@ -4285,7 +4410,7 @@ void TabCompetition::mergeCompetition(gdioutput &gdi) {
gdi.refresh(); gdi.refresh();
} }
else if (bi.id == "Browse") { else if (bi.id == "Browse") {
wstring fn = gdi.browseForOpen({ make_pair(L"xml", L"*.xml") }, L"xml"); wstring fn = gdi.browseForOpen({ make_pair(L"MeOS-data", L"*.meosxml;*.xml;*.bu?") }, L"meosxml");
if (fn.empty()) if (fn.empty())
return; return;
@ -4296,7 +4421,7 @@ void TabCompetition::mergeCompetition(gdioutput &gdi) {
else if (bi.id == "Read") { else if (bi.id == "Read") {
mergeEvent = make_shared<oEvent>(gdi); mergeEvent = make_shared<oEvent>(gdi);
if (!mergeEvent->open(tc->mergeFile, true, true)) if (!mergeEvent->open(tc->mergeFile, true, true, false))
return; return;
gdi.restore("merge", false); gdi.restore("merge", false);
@ -4384,7 +4509,7 @@ void TabCompetition::mergeCompetition(gdioutput &gdi) {
baseEvent = make_shared<oEvent>(gdi); baseEvent = make_shared<oEvent>(gdi);
bool ok = false; bool ok = false;
try { try {
ok = baseEvent->open(base, true, true); ok = baseEvent->open(base, true, true, false);
ok = true; ok = true;
} }
catch (...) { catch (...) {

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -60,6 +60,8 @@ class TabCompetition :
void copyrightLine(gdioutput &gdi) const; void copyrightLine(gdioutput &gdi) const;
void loadAboutPage(gdioutput &gdi) const; void loadAboutPage(gdioutput &gdi) const;
void updateWarning(gdioutput &gdi) const;
int organizorId; int organizorId;
int lastChangeClassType; int lastChangeClassType;
@ -118,6 +120,7 @@ class TabCompetition :
void meosFeatures(gdioutput &gdi, bool newGuide); void meosFeatures(gdioutput &gdi, bool newGuide);
void newCompetitionGuide(gdioutput &gdi, int step); void newCompetitionGuide(gdioutput &gdi, int step);
void createNewCmp(gdioutput &gdi, bool useExisting);
void entryForm(gdioutput &gdi, bool isGuide); void entryForm(gdioutput &gdi, bool isGuide);
FlowOperation saveEntries(gdioutput &gdi, bool removeRemoved, bool isGuide); FlowOperation saveEntries(gdioutput &gdi, bool removeRemoved, bool isGuide);
@ -132,6 +135,8 @@ class TabCompetition :
void entryChoice(gdioutput &gdi); void entryChoice(gdioutput &gdi);
void createCompetition(gdioutput &gdi); void createCompetition(gdioutput &gdi);
void importDefaultHiredCards(gdioutput& gdi);
void listBackups(gdioutput &gdi); void listBackups(gdioutput &gdi);
shared_ptr<GuiHandler> mergeHandler; shared_ptr<GuiHandler> mergeHandler;

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -55,35 +55,46 @@ void TabControl::selectControl(gdioutput &gdi, pControl pc)
if (pc) { if (pc) {
pc->synchronize(); pc->synchronize();
if (pc->getStatus() == oControl::StatusStart || if (oControl::isSpecialControl(pc->getStatus())) {
pc->getStatus() == oControl::StatusFinish) {
gdi.selectItemByData("Controls", pc->getId()); gdi.selectItemByData("Controls", pc->getId());
gdi.selectItemByData("Status", oControl::StatusOK); gdi.selectItemByData("Status", int(oControl::ControlStatus::StatusOK));
gdi.setText("ControlID", makeDash(L"-"), true); gdi.setText("ControlID", makeDash(L"-"), true);
gdi.setText("Code", L""); gdi.setText("Code", L"");
gdi.setText("Name", pc->getName()); gdi.setText("Name", pc->getName());
gdi.setText("TimeAdjust", L"00:00"); gdi.setText("TimeAdjust", pc->getTimeAdjustS());
gdi.setText("MinTime", makeDash(L"-")); gdi.setText("MinTime", makeDash(L"-"));
gdi.setText("Point", L""); gdi.setText("Point", L"");
gdi.disableInput("Visitors");
gdi.disableInput("Courses");
controlId = pc->getId(); controlId = pc->getId();
gdi.enableInput("Remove"); gdi.enableInput("Remove");
gdi.enableInput("Save"); gdi.enableInput("Save");
gdi.enableEditControls(false); gdi.enableEditControls(false);
gdi.enableInput("Name"); gdi.enableInput("Name");
if (pc->getUnitCode() > 0) {
gdi.setText("Code", itow(pc->getUnitCode()));
gdi.enableInput("TimeAdjust");
gdi.setText("Info", lang.tl("Du kan justera tiden för en viss enhet"), true);
}
else {
gdi.setText("Info", L"", true);
}
} }
else { else {
gdi.selectItemByData("Controls", pc->getId()); gdi.selectItemByData("Controls", pc->getId());
gdi.selectItemByData("Status", pc->getStatus()); gdi.selectItemByData("Status", int(pc->getStatus()));
const int numVisit = pc->getNumVisitors(true); const int numVisit = pc->getNumVisitors(true);
const int numVisitExp = pc->getNumVisitors(false); const int numVisitExp = pc->getNumVisitors(false);
wstring info; wstring info;
if (numVisit > 0) { if (numVisit > 0) {
info = L"Antal besökare X, genomsnittlig bomtid Y, största bomtid Z#" + info = L"Antal besökare X, genomsnittlig bomtid Y, största bomtid Z#" +
itow(numVisit) + L" (" + itow(numVisitExp) + L")#" + getTimeMS(pc->getMissedTimeTotal() / numVisit) + itow(numVisit) + L" (" + itow(numVisitExp) + L")#" + formatTimeMS(pc->getMissedTimeTotal() / numVisit, false, SubSecond::Off) +
L"#" + getTimeMS(pc->getMissedTimeMax()); L"#" + formatTimeMS(pc->getMissedTimeMax(), false, SubSecond::Off);
} }
else if (numVisitExp > 0) { else if (numVisitExp > 0) {
info = L"Förväntat antal besökare: X#" + itow(numVisitExp); info = L"Förväntat antal besökare: X#" + itow(numVisitExp);
@ -108,19 +119,20 @@ void TabControl::selectControl(gdioutput &gdi, pControl pc)
gdi.enableEditControls(true); gdi.enableEditControls(true);
oControl::ControlStatus st = pc->getStatus(); oControl::ControlStatus st = pc->getStatus();
if (st == oControl::StatusRogaining || st == oControl::StatusNoTiming || st == oControl::StatusBadNoTiming) if (st == oControl::ControlStatus::StatusRogaining || st == oControl::ControlStatus::StatusRogainingRequired ||
st == oControl::ControlStatus::StatusNoTiming || st == oControl::ControlStatus::StatusBadNoTiming)
gdi.disableInput("MinTime"); gdi.disableInput("MinTime");
if (st == oControl::StatusNoTiming || st == oControl::StatusBadNoTiming) if (st == oControl::ControlStatus::StatusNoTiming || st == oControl::ControlStatus::StatusBadNoTiming)
gdi.disableInput("TimeAdjust"); gdi.disableInput("TimeAdjust");
if (gdi.hasWidget("Point") && st != oControl::StatusRogaining) if (gdi.hasWidget("Point") && st != oControl::ControlStatus::StatusRogaining && st != oControl::ControlStatus::StatusRogainingRequired)
gdi.disableInput("Point"); gdi.disableInput("Point");
} }
} }
else { else {
gdi.selectItemByData("Controls", -1); gdi.selectItemByData("Controls", -1);
gdi.selectItemByData("Status", oControl::StatusOK); gdi.selectItemByData("Status", int(oControl::ControlStatus::StatusOK));
gdi.setText("Code", L""); gdi.setText("Code", L"");
gdi.setText("Name", L""); gdi.setText("Name", L"");
controlId = 0; controlId = 0;
@ -145,26 +157,58 @@ int ControlsCB(gdioutput *gdi, int type, void *data)
return tc.controlCB(*gdi, type, data); return tc.controlCB(*gdi, type, data);
} }
void TabControl::save(gdioutput &gdi) void TabControl::save(gdioutput &gdi) {
{
if (controlId==0) if (controlId==0)
return; return;
DWORD pcid = controlId; pControl pc = oe->getControl(controlId, false, true);
pControl pc;
pc = oe->getControl(pcid, false);
if (!pc) if (!pc)
throw std::exception("Internal error"); throw std::exception("Internal error");
if (pc->getStatus() != oControl::StatusFinish && pc->getStatus() != oControl::StatusStart) {
if (!pc->isAddedToEvent()) {
if (gdi.getText("TimeAdjust") == pc->getTimeAdjustS()
&& gdi.getText("Name") == pc->getName())
return; // Virtual control with no change.
oe->synchronizeList(oListId::oLControlId);
pc = oe->getControl(controlId, false, true);
if (!pc->isAddedToEvent())
pc = oe->addControl(*pc);
if (!pc)
throw std::exception("Internal error");
}
bool defaultName = false;
if (!oControl::isSpecialControl(pc->getStatus())) {
int oldFirst = pc->getFirstNumber();
if (!pc->setNumbers(gdi.getText("Code"))) if (!pc->setNumbers(gdi.getText("Code")))
gdi.alert("Kodsiffran måste vara ett heltal. Flera kodsiffror måste separeras med komma."); gdi.alert("Kodsiffran måste vara ett heltal. Flera kodsiffror måste separeras med komma.");
int newFirst = pc->getFirstNumber();
if (oldFirst != newFirst && pc->getId() == oldFirst) {
// Update id if possible (make new control and remove old)
if (!oe->isControlUsed(pc->getId()) && oe->getControl(newFirst) == nullptr) {
pc->setName(gdi.getText("Name"));
if (!pc->hasName())
defaultName = true;
pc = oe->addControl(newFirst, newFirst, L"");
pc->synchronize();
controlId = pc->getId();
pc->setNumbers(gdi.getText("Code"));
oe->removeControl(oldFirst);
}
}
pc->setStatus(oControl::ControlStatus(gdi.getSelectedItem("Status").first)); pc->setStatus(oControl::ControlStatus(gdi.getSelectedItem("Status").first));
pc->setTimeAdjust(gdi.getText("TimeAdjust")); pc->setTimeAdjust(gdi.getText("TimeAdjust"));
if (pc->getStatus() != oControl::StatusRogaining) { if (pc->getStatus() != oControl::ControlStatus::StatusRogaining && pc->getStatus() != oControl::ControlStatus::StatusRogainingRequired) {
if (pc->getStatus() != oControl::StatusNoTiming && pc->getStatus() != oControl::StatusBadNoTiming) if (pc->getStatus() != oControl::ControlStatus::StatusNoTiming && pc->getStatus() != oControl::ControlStatus::StatusBadNoTiming)
pc->setMinTime(gdi.getText("MinTime")); pc->setMinTime(gdi.getText("MinTime"));
pc->setRogainingPoints(0); pc->setRogainingPoints(0);
} }
@ -175,12 +219,65 @@ void TabControl::save(gdioutput &gdi)
} }
} }
} }
else if (pc->isUnit()) {
// Ensure cache is up-to-date
auto type = pc->getUnitType();
int oldAdjust = oe->getUnitAdjustment(type, pc->getUnitCode());
pc->setName(gdi.getText("Name")); if (pc->setTimeAdjust(gdi.getText("TimeAdjust"))) {
// Cache is not updated. No new adjustment applied
assert(oldAdjust == oe->getUnitAdjustment(type, pc->getUnitCode()));
vector<pair<pRunner, pFreePunch>> adjustList;
if (type == oPunch::SpecialPunch::PunchStart) {
auto pList = oe->getPunchesByType(type, pc->getUnitCode());
for (auto p : pList) {
pRunner r = oe->getRunnerByCardNo(p->getCardNo(), p->getTimeInt(), oEvent::CardLookupProperty::Any);
if (r && !r->getCard() && r->getStartTime() == p->getTimeInt()) {
// Need not adjust runners with card.
adjustList.emplace_back(r, p);
}
}
}
else if (type == oPunch::SpecialPunch::PunchFinish) {
auto pList = oe->getPunchesByType(type, pc->getUnitCode());
for (auto p : pList) {
pRunner r = oe->getRunnerByCardNo(p->getCardNo(), p->getTimeInt(), oEvent::CardLookupProperty::Any);
if (r && !r->getCard() && r->getFinishTime() == p->getTimeInt()) {
// Need not adjust runners with card.
adjustList.emplace_back(r, p);
}
}
}
// Clear cache to make sure adjustment takes effect
pc->clearCache();
oe->clearUnitAdjustmentCache();
// With new adjustment applied
assert(oldAdjust != oe->getUnitAdjustment(type, pc->getUnitCode()));
for (auto& rp : adjustList) {
if (type == oPunch::SpecialPunch::PunchStart) {
rp.first->setStartTime(rp.second->getTimeInt(), true, oBase::ChangeType::Update, true);
}
else if (type == oPunch::SpecialPunch::PunchFinish) {
rp.first->setFinishTime(rp.second->getTimeInt());
}
rp.first->synchronize(true);
}
}
}
if (!defaultName)
pc->setName(gdi.getText("Name"));
pc->synchronize(); pc->synchronize();
vector< pair<wstring, size_t> > d;
oe->fillControls(d, oEvent::CTAll); vector<pair<wstring, size_t>> d;
oe->fillControls(d, oEvent::ControlType::All);
gdi.addItem("Controls", d); gdi.addItem("Controls", d);
oe->reEvaluateAll(set<int>(), true); oe->reEvaluateAll(set<int>(), true);
@ -239,8 +336,8 @@ void TabControl::courseTable(Table &table) const {
void TabControl::visitorTable(Table &table) const { void TabControl::visitorTable(Table &table) const {
vector<pCard> c; vector<pCard> c;
oe->getCards(c); oe->getCards(c, true, false);
pControl pc = oe->getControl(controlId, false); pControl pc = oe->getControl(controlId, false, true);
if (!pc) if (!pc)
return; return;
@ -297,7 +394,7 @@ void TabControl::visitorTable(Table &table) const {
lang.tl("Ja") : lang.tl("Nej"), false); lang.tl("Ja") : lang.tl("Nej"), false);
table.set(row++, it, TID_CARD, it.getCardNoString(), false); table.set(row++, it, TID_CARD, it.getCardNoString(), false);
table.set(row++, it, TID_STATUS, punch->getTime(), false); table.set(row++, it, TID_STATUS, punch->getTime(false, SubSecond::Auto), false);
table.set(row++, it, TID_CONTROL, punch->getType(), false); table.set(row++, it, TID_CONTROL, punch->getType(), false);
table.set(row++, it, TID_CODES, j>0 ? p[j-1]->getType() : L"-", true); table.set(row++, it, TID_CODES, j>0 ? p[j-1]->getType() : L"-", true);
} }
@ -316,15 +413,24 @@ int TabControl::controlCB(gdioutput &gdi, int type, void *data)
bool rogaining = false; bool rogaining = false;
if (controlId>0) { if (controlId>0) {
save(gdi); save(gdi);
pControl pc = oe->getControl(controlId, false); pControl pc = oe->getControl(controlId, false, true);
rogaining = pc && pc->getStatus() == oControl::StatusRogaining; rogaining = pc && (pc->getStatus() == oControl::ControlStatus::StatusRogaining || pc->getStatus() == oControl::ControlStatus::StatusRogainingRequired);
} }
pControl pc = oe->addControl(0,oe->getNextControlNumber(), L""); int nextCtrl = oe->getNextControlNumber();
int nextId = nextCtrl;
if (oe->getControl(nextId)) {
nextId += 1000;
if (oe->getControl(nextId))
nextId = 0; // Use default
}
pControl pc = oe->addControl(nextId, nextCtrl, L"");
if (rogaining) if (rogaining)
pc->setStatus(oControl::StatusRogaining); pc->setStatus(oControl::ControlStatus::StatusRogaining);
pc->synchronize();
vector< pair<wstring, size_t> > d; vector< pair<wstring, size_t> > d;
oe->fillControls(d, oEvent::CTAll); oe->fillControls(d, oEvent::ControlType::All);
gdi.addItem("Controls", d); gdi.addItem("Controls", d);
selectControl(gdi, pc); selectControl(gdi, pc);
} }
@ -342,7 +448,7 @@ int TabControl::controlCB(gdioutput &gdi, int type, void *data)
oe->removeControl(cid); oe->removeControl(cid);
vector< pair<wstring, size_t> > d; vector< pair<wstring, size_t> > d;
oe->fillControls(d, oEvent::CTAll); oe->fillControls(d, oEvent::ControlType::All);
gdi.addItem("Controls", d); gdi.addItem("Controls", d);
selectControl(gdi, 0); selectControl(gdi, 0);
} }
@ -409,16 +515,17 @@ int TabControl::controlCB(gdioutput &gdi, int type, void *data)
if (gdi.isInputChanged("")) if (gdi.isInputChanged(""))
save(gdi); save(gdi);
pControl pc=oe->getControl(bi.data, false); pControl pc=oe->getControl(bi.data, false, true);
if (!pc) if (!pc)
throw std::exception("Internal error"); throw std::exception("Internal error");
selectControl(gdi, pc); selectControl(gdi, pc);
} }
else if (bi.id == "Status" ) { else if (bi.id == "Status" ) {
gdi.setInputStatus("MinTime", bi.data != oControl::StatusRogaining && bi.data != oControl::StatusNoTiming && bi.data != oControl::StatusBadNoTiming, true); oControl::ControlStatus st = (oControl::ControlStatus)bi.data;
gdi.setInputStatus("Point", bi.data == oControl::StatusRogaining, true); gdi.setInputStatus("MinTime", st != oControl::ControlStatus::StatusRogaining && st != oControl::ControlStatus::StatusRogainingRequired && st != oControl::ControlStatus::StatusNoTiming && st != oControl::ControlStatus::StatusBadNoTiming, true);
gdi.setInputStatus("TimeAdjust", bi.data != oControl::StatusNoTiming && bi.data != oControl::StatusBadNoTiming, true); gdi.setInputStatus("Point", st == oControl::ControlStatus::StatusRogaining || st != oControl::ControlStatus::StatusRogainingRequired, true);
gdi.setInputStatus("TimeAdjust", st != oControl::ControlStatus::StatusNoTiming && st != oControl::ControlStatus::StatusBadNoTiming, true);
} }
} }
else if (type==GUI_CLEAR) { else if (type==GUI_CLEAR) {
@ -454,10 +561,10 @@ bool TabControl::loadPage(gdioutput &gdi)
gdi.pushY(); gdi.pushY();
gdi.addListBox("Controls", 250, 530, ControlsCB).isEdit(false).ignore(true); gdi.addListBox("Controls", 250, 530, ControlsCB).isEdit(false).ignore(true);
gdi.setTabStops("Controls", 40, 160); gdi.setTabStops("Controls", 44, 160);
vector< pair<wstring, size_t> > d; vector< pair<wstring, size_t> > d;
oe->fillControls(d, oEvent::CTAll); oe->fillControls(d, oEvent::ControlType::All);
gdi.addItem("Controls", d); gdi.addItem("Controls", d);
gdi.newColumn(); gdi.newColumn();
@ -515,7 +622,7 @@ bool TabControl::loadPage(gdioutput &gdi)
gdi.dropLine(1.5); gdi.dropLine(1.5);
gdi.addString("", 10, "help:89064"); gdi.addString("", 10, "help:89064");
selectControl(gdi, oe->getControl(controlId, false)); selectControl(gdi, oe->getControl(controlId, false, true));
gdi.setOnClearCb(ControlsCB); gdi.setOnClearCb(ControlsCB);

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -459,7 +459,7 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data)
ext.push_back(make_pair(L"IOF CourseData, version 3.0 (xml)", L"*.xml")); ext.push_back(make_pair(L"IOF CourseData, version 3.0 (xml)", L"*.xml"));
wstring save = gdi.browseForSave(ext, L"xml", FilterIndex); wstring save = gdi.browseForSave(ext, L"xml", FilterIndex);
if (save.length()>0) { if (save.length()>0) {
IOF30Interface iof30(oe, false); IOF30Interface iof30(oe, false, false);
xmlparser xml; xmlparser xml;
xml.openOutput(save.c_str(), false); xml.openOutput(save.c_str(), false);
iof30.writeCourses(xml); iof30.writeCourses(xml);
@ -477,15 +477,19 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data)
gdi.disableInput("Cancel"); gdi.disableInput("Cancel");
gdi.disableInput("BrowseCourse"); gdi.disableInput("BrowseCourse");
gdi.disableInput("AddClasses"); gdi.disableInput("AddClasses");
gdi.disableInput("CreateClasses");
try { try {
TabCourse::runCourseImport(gdi, filename, oe, gdi.isChecked("AddClasses")); TabCourse::runCourseImport(gdi, filename, oe,
gdi.isChecked("AddClasses"),
gdi.isChecked("CreateClasses"));
} }
catch (std::exception &) { catch (const std::exception &) {
gdi.enableInput("DoImportCourse"); gdi.enableInput("DoImportCourse");
gdi.enableInput("Cancel"); gdi.enableInput("Cancel");
gdi.enableInput("BrowseCourse"); gdi.enableInput("BrowseCourse");
gdi.enableInput("AddClasses"); gdi.enableInput("AddClasses");
gdi.enableInput("CreateClasses");
throw; throw;
} }
gdi.addButton("Cancel", "OK", CourseCB); gdi.addButton("Cancel", "OK", CourseCB);
@ -525,8 +529,8 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data)
gdi.pushX(); gdi.pushX();
gdi.fillRight(); gdi.fillRight();
int firstStart = 3600; int firstStart = timeConstHour;
int interval = 2*60; int interval = 2*timeConstMinute;
int vac = 1; int vac = 1;
gdi.addInput("FirstStart", oe->getAbsTime(firstStart), 10, 0, L"Första start:"); gdi.addInput("FirstStart", oe->getAbsTime(firstStart), 10, 0, L"Första start:");
gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):"); gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):");
@ -891,14 +895,14 @@ bool TabCourse::loadPage(gdioutput &gdi) {
} }
void TabCourse::runCourseImport(gdioutput& gdi, const wstring &filename, void TabCourse::runCourseImport(gdioutput& gdi, const wstring &filename,
oEvent *oe, bool addClasses) { oEvent *oe, bool addToClasses, bool createClasses) {
if (csvparser::iscsv(filename) != csvparser::CSV::NoCSV) { if (csvparser::iscsv(filename) != csvparser::CSV::NoCSV) {
gdi.fillRight(); gdi.fillRight();
gdi.pushX(); gdi.pushX();
gdi.addString("", 0, "Importerar OCAD csv-fil..."); gdi.addString("", 0, "Importerar OCAD csv-fil...");
gdi.refreshFast(); gdi.refreshFast();
csvparser csv; csvparser csv;
if (csv.importOCAD_CSV(*oe, filename, addClasses)) { if (csv.importOCAD_CSV(*oe, filename, addToClasses)) {
gdi.addString("", 1, "Klart.").setColor(colorGreen); gdi.addString("", 1, "Klart.").setColor(colorGreen);
} }
else gdi.addString("", 0, "Operationen misslyckades.").setColor(colorRed); else gdi.addString("", 0, "Operationen misslyckades.").setColor(colorRed);
@ -906,12 +910,64 @@ void TabCourse::runCourseImport(gdioutput& gdi, const wstring &filename,
gdi.dropLine(2.5); gdi.dropLine(2.5);
gdi.fillDown(); gdi.fillDown();
} }
else if (filename.find(L".txt") != wstring::npos || filename.find(L".TXT") != wstring::npos) {
ifstream fin(filename);
if (!fin.good())
throw meosException(L"Cannot read " + filename);
char bf[2048];
vector<string> sw;
int importedC = oe->getNumCourses() + 1;
int line = 0;
while (fin.good()) {
fin.getline(bf, 2048);
if (strlen(bf) < 2)
continue;
if (0 == line && uint8_t(bf[0]) == 0xEF && uint8_t(bf[1]) == 0xBB && uint8_t(bf[2]) == 0xBF)
split(bf+3, " ;,", sw);
else
split(bf, " ;,", sw);
line++;
if (sw.size() <= 1)
continue;
wstring name;
int first = 0;
if (atoi(sw[0].c_str()) < 30 && trim(sw[0]).length() > 2) {
name = gdioutput::fromUTF8(trim(sw[0]));
first = 1;
}
if (name.empty())
name = lang.tl("Bana X#" + itos(importedC++));
string cs;
for (int i = first; i < sw.size(); i++) {
if (trim(sw[i]).empty())
continue;
int c = atoi(sw[i].c_str());
if (c >= 30 && c < 1000)
cs += itos(c) + " ";
else {
throw meosException("Kan inte tolka 'X' som en bana#" + string(bf));
}
}
pCourse pc = oe->addCourse(name);
pc->importControls(cs, true, false);
pc->synchronize();
}
fin.close();
}
else { else {
set<int> noFilter; set<int> noFilter;
string noType; string noType;
oe->importXML_EntryData(gdi, filename.c_str(), addClasses, false, noFilter, noType); oe->importXML_EntryData(gdi, filename.c_str(), addToClasses, false, noFilter, noType);
} }
if (addClasses) { if (addToClasses) {
// There is specific course-class matching inside the import of each format, // There is specific course-class matching inside the import of each format,
// that uses additional information. Here we try to match based on a generic approach. // that uses additional information. Here we try to match based on a generic approach.
vector<pClass> cls; vector<pClass> cls;
@ -1012,6 +1068,34 @@ void TabCourse::runCourseImport(gdioutput& gdi, const wstring &filename,
gdi.dropLine(); gdi.dropLine();
} }
if (createClasses) {
vector<pClass> cls;
vector<pCourse> crs;
oe->getClasses(cls, false);
oe->getCourses(crs);
unordered_set<int> usedCourseId;
vector<pCourse> usedCrs;
for (size_t k = 0; k < cls.size(); k++) {
cls[k]->getCourses(-1, usedCrs);
for (pCourse pc : usedCrs)
usedCourseId.insert(pc->getId());
}
set<wstring> usedNames;
for (pCourse pc : crs) {
if (usedCourseId.count(pc->getId()))
continue;
pClass matchCls = oe->getClassCreate(-1, pc->getName(), usedNames);
if (!matchCls || matchCls->getCourse(false)) {
oe->addClass(pc->getName() + lang.tl(" Bana"), pc->getId());
}
else {
matchCls->setCourse(pc);
}
}
}
gdi.addButton(gdi.getWidth()+20, 45, gdi.scaleLength(baseButtonWidth), gdi.addButton(gdi.getWidth()+20, 45, gdi.scaleLength(baseButtonWidth),
"Print", "Skriv ut...", CourseCB, "Print", "Skriv ut...", CourseCB,
"Skriv ut listan.", true, false); "Skriv ut listan.", true, false);
@ -1043,6 +1127,7 @@ void TabCourse::setupCourseImport(gdioutput& gdi, GUICALLBACK cb) {
gdi.fillDown(); gdi.fillDown();
gdi.addCheckbox("AddClasses", "Lägg till klasser", 0, true); gdi.addCheckbox("AddClasses", "Lägg till klasser", 0, true);
gdi.addCheckbox("CreateClasses", "Skapa en klass för varje bana", 0, false);
gdi.dropLine(); gdi.dropLine();
gdi.fillRight(); gdi.fillRight();
@ -1060,9 +1145,9 @@ void TabCourse::fillCourseControls(gdioutput &gdi, const wstring &ctrl) {
vector< pair<wstring, size_t> > item; vector< pair<wstring, size_t> > item;
map<int, int> used; map<int, int> used;
for (size_t k = 0; k < nr.size(); k++) { for (size_t k = 0; k < nr.size(); k++) {
pControl pc = oe->getControl(nr[k], false); pControl pc = oe->getControl(nr[k], false, false);
if (pc) { if (pc) {
if (pc->getStatus() == oControl::StatusOK) if (pc->getStatus() == oControl::ControlStatus::StatusOK)
++used[pc->getFirstNumber()]; ++used[pc->getFirstNumber()];
} }
else else
@ -1085,7 +1170,7 @@ void TabCourse::fillCourseControls(gdioutput &gdi, const wstring &ctrl) {
void TabCourse::fillOtherCourses(gdioutput &gdi, oCourse &crs, bool withLoops) { void TabCourse::fillOtherCourses(gdioutput &gdi, oCourse &crs, bool withLoops) {
vector< pair<wstring, size_t> > ac; vector< pair<wstring, size_t> > ac;
oe->fillCourses(ac, true); oe->getCourses(ac, L"", true);
set<int> skipped; set<int> skipped;
skipped.insert(crs.getId()); skipped.insert(crs.getId());
pCourse longer = crs.getLongerVersion(); pCourse longer = crs.getLongerVersion();
@ -1223,7 +1308,7 @@ wstring TabCourse::encodeCourse(const wstring &in, bool rogaining, bool firstSta
} }
const wstring &TabCourse::formatControl(int id, wstring &bf) const { const wstring &TabCourse::formatControl(int id, wstring &bf) const {
pControl ctrl = oe->getControl(id, false); pControl ctrl = oe->getControl(id, false, true);
if (ctrl) { if (ctrl) {
bf = ctrl->getString(); bf = ctrl->getString();
return bf; return bf;
@ -1231,3 +1316,4 @@ const wstring &TabCourse::formatControl(int id, wstring &bf) const {
else else
return itow(id); return itow(id);
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -70,7 +70,7 @@ public:
~TabCourse(void); ~TabCourse(void);
static void runCourseImport(gdioutput& gdi, const wstring &filename, static void runCourseImport(gdioutput& gdi, const wstring &filename,
oEvent *oe, bool addClasses); oEvent *oe, bool addToClasses, bool createClasses);
static void setupCourseImport(gdioutput& gdi, GUICALLBACK cb); static void setupCourseImport(gdioutput& gdi, GUICALLBACK cb);

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -341,7 +341,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
} }
else if (bi.id=="Copy") { else if (bi.id=="Copy") {
ostringstream fout; ostringstream fout;
HTMLWriter::writeTableHTML(gdi, fout, L"MeOS", true, 0, 1.0); HTMLWriter::writeTableHTML(gdi, fout, L"MeOS", false, L"", true, 0, 1.0);
string res = fout.str(); string res = fout.str();
gdi.copyToClipboard(res, L""); gdi.copyToClipboard(res, L"");
} }
@ -570,32 +570,44 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
else if (bi.id=="CancelPS") { else if (bi.id=="CancelPS") {
gdi.getTabs().get(TabType(bi.getExtraInt()))->loadPage(gdi); gdi.getTabs().get(TabType(bi.getExtraInt()))->loadPage(gdi);
} }
else if (bi.id=="SavePS") { else if (bi.id == "SavePS") {
string ctype; string ctype;
gdi.getData("Type", ctype); gdi.getData("Type", ctype);
saveExtraLines(*oe, ctype.c_str(), gdi); saveExtraLines(*oe, ctype.c_str(), gdi);
if (gdi.hasWidget("SplitAnalysis")) { if (gdi.hasWidget("SplitAnalysis")) {
int aflag = (gdi.isChecked("SplitAnalysis") ? 0 : 1) + (gdi.isChecked("Speed") ? 0 : 2) int aflag = (gdi.isChecked("SplitAnalysis") ? 0 : 1) + (gdi.isChecked("Speed") ? 0 : 2)
+ (gdi.isChecked("Results") ? 0 : 4); + (gdi.isChecked("Results") ? 0 : 4);
oe->getDI().setInt("Analysis", aflag); oe->getDI().setInt("Analysis", aflag);
} }
if (gdi.hasWidget("SplitPrintList")) {
if (gdi.hasWidget("WideFormat")) { auto res = gdi.getSelectedItem("SplitPrintList");
bool wide = gdi.isChecked("WideFormat"); if (res.second) {
oe->setProperty("WideSplitFormat", wide); if (res.first == -10)
oe->getDI().setString("SplitPrint", L"");
if (wide && gdi.hasWidget("NumPerPage")) { else {
pair<int, bool> res = gdi.getSelectedItem("NumPerPage"); EStdListType type = oe->getListContainer().getType(res.first);
if (res.second) string id = oe->getListContainer().getUniqueId(type);
oe->setProperty("NumSplitsOnePage", res.first); oe->getDI().setString("SplitPrint", gdioutput::widen(id));
}
}
}
if (gdi.hasWidget("WideFormat")) {
bool wide = gdi.isChecked("WideFormat");
oe->setProperty("WideSplitFormat", wide);
int no = gdi.getTextNo("MaxWaitTime"); if (wide && gdi.hasWidget("NumPerPage")) {
if (no >= 0) pair<int, bool> res = gdi.getSelectedItem("NumPerPage");
oe->setProperty("SplitPrintMaxWait", no); if (res.second)
} oe->setProperty("NumSplitsOnePage", res.first);
}
int no = gdi.getTextNo("MaxWaitTime");
if (no >= 0)
oe->setProperty("SplitPrintMaxWait", no);
}
}
gdi.getTabs().get(TabType(bi.getExtraInt()))->loadPage(gdi); gdi.getTabs().get(TabType(bi.getExtraInt()))->loadPage(gdi);
} }
else if (bi.id == "PrinterSetup") { else if (bi.id == "PrinterSetup") {
@ -1071,11 +1083,21 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
return listCB(gdi, GUI_BUTTON, &bi); return listCB(gdi, GUI_BUTTON, &bi);
} }
gdi.clearPage(false); gdi.clearPage(false);
gdi.addString("", boldLarge, "Tillgängliga listor"); gdi.addString("", boldLarge, "Tillgängliga listor");
int xx = gdi.getCX() + gdi.scaleLength(360);
int bx = gdi.getCX(); int bx = gdi.getCX();
int xx = bx + gdi.scaleLength(360);
TextInfo ti;
for (size_t k = 0; k < installedLists.size(); k++) {
ti.text = installedLists[k].first;
gdi.calcStringSize(ti);
xx = max(xx, bx + ti.realWidth + 10);
}
for (size_t k = 0; k < lists.size(); k++) {
ti.text = lists[k].first;
gdi.calcStringSize(ti);
xx = max(xx, bx + ti.realWidth + 10);
}
if (!installedLists.empty()) { if (!installedLists.empty()) {
gdi.dropLine(); gdi.dropLine();
gdi.addString("", 1, "Listor i tävlingen"); gdi.addString("", 1, "Listor i tävlingen");
@ -1141,8 +1163,13 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
xmlobject xlist = xml.getObject(0); xmlobject xlist = xml.getObject(0);
oe->synchronize(); oe->synchronize();
oe->getListContainer().load(MetaListContainer::ExternalList, xlist, false); oe->getListContainer().load(MetaListContainer::ExternalList, xlist, false);
oe->synchronize(true);
set<uint64_t> imgUsed;
oe->getListContainer().getUsedImages(imgUsed);
for (uint64_t id : imgUsed)
oe->saveImage(id);
oe->synchronize(true);
loadPage(gdi); loadPage(gdi);
} }
} }
@ -1218,6 +1245,12 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
oe->synchronize(false); oe->synchronize(false);
oe->getListContainer().load(MetaListContainer::ExternalList, xlist, false); oe->getListContainer().load(MetaListContainer::ExternalList, xlist, false);
set<uint64_t> imgUsed;
oe->getListContainer().getUsedImages(imgUsed);
for (uint64_t id : imgUsed)
oe->saveImage(id);
oe->synchronize(true); oe->synchronize(true);
oe->loadGeneralResults(true, false); oe->loadGeneralResults(true, false);
} }
@ -1286,7 +1319,7 @@ pair<gdioutput *, TabList *> TabList::makeOwnWindow(gdioutput &gdi) {
void TabList::enableFromTo(oEvent &oe, gdioutput &gdi, bool from, bool to) { void TabList::enableFromTo(oEvent &oe, gdioutput &gdi, bool from, bool to) {
vector< pair<wstring, size_t> > d; vector< pair<wstring, size_t> > d;
oe.fillControls(d, oEvent::CTCourseControl); oe.fillControls(d, oEvent::ControlType::CourseControl);
if (from) { if (from) {
gdi.enableInput("ResultSpecialFrom"); gdi.enableInput("ResultSpecialFrom");
@ -2216,7 +2249,7 @@ void TabList::settingsResultList(gdioutput &gdi)
vector< pair<wstring, size_t> > lists; vector< pair<wstring, size_t> > lists;
vector< pair<wstring, size_t> > dlists; vector< pair<wstring, size_t> > dlists;
const MetaListContainer &lc = oe->getListContainer(); const MetaListContainer &lc = oe->getListContainer();
lc.getLists(dlists, false, true, !oe->hasTeam()); lc.getLists(dlists, false, true, !oe->hasTeam(), false);
set<int> usedListIx; set<int> usedListIx;
map<string, int> tag2ListIx; map<string, int> tag2ListIx;
for (size_t k = 0; k < dlists.size(); k++) { for (size_t k = 0; k < dlists.size(); k++) {
@ -2725,31 +2758,72 @@ void TabList::splitPrintSettings(oEvent &oe, gdioutput &gdi, bool setupPrinter,
gdi.addString("", boldLarge, "Inställningar startbevis"); gdi.addString("", boldLarge, "Inställningar startbevis");
gdi.dropLine(); gdi.dropLine();
gdi.fillRight();
gdi.pushX(); gdi.pushX();
if (setupPrinter) {
gdi.addButton("PrinterSetup", "Skrivare...", ListsCB, "Skrivarinställningar");
gdi.dropLine(0.3);
}
if (!oe.empty() && type == Splits) { if (!oe.empty() && type == Splits) {
bool withSplitAnalysis = (oe.getDCI().getInt("Analysis") & 1) == 0; gdi.fillRight();
gdi.addSelection("SplitPrintList", 200, 200, nullptr, L"Sträcktidslista:");
if (setupPrinter) {
gdi.dropLine(0.9);
gdi.addButton("PrinterSetup", "Skrivare...", ListsCB, "Skrivarinställningar");
gdi.dropLine(2.8);
}
else {
gdi.dropLine(3);
}
gdi.fillDown();
gdi.popX();
gdi.addString("", 10, "info:customsplitprint");
gdi.dropLine();
vector<pair<wstring, size_t>> lists;
oe.getListContainer().getLists(lists, false, false, false, true);
lists.insert(lists.begin(), make_pair(lang.tl("Standard"), -10));
gdi.addItem("SplitPrintList", lists);
wstring listId = oe.getDCI().getString("SplitPrint");
EStdListType type = oe.getListContainer().getCodeFromUnqiueId(gdioutput::narrow(listId));
if (type == EStdListType::EStdNone)
gdi.selectFirstItem("SplitPrintList");
else {
for (auto& t : lists) {
if (type == oe.getListContainer().getType(t.second))
gdi.selectItemByData("SplitPrintList", t.second);
}
}
//if ()
/* bool withSplitAnalysis = (oe.getDCI().getInt("Analysis") & 1) == 0;
bool withSpeed = (oe.getDCI().getInt("Analysis") & 2) == 0; bool withSpeed = (oe.getDCI().getInt("Analysis") & 2) == 0;
bool withResult = (oe.getDCI().getInt("Analysis") & 4) == 0; bool withResult = (oe.getDCI().getInt("Analysis") & 4) == 0;
gdi.addCheckbox("SplitAnalysis", "Med sträcktidsanalys", 0, withSplitAnalysis); gdi.addCheckbox("SplitAnalysis", "Med sträcktidsanalys", 0, withSplitAnalysis);
gdi.addCheckbox("Speed", "Med km-tid", 0, withSpeed); gdi.addCheckbox("Speed", "Med km-tid", 0, withSpeed);
gdi.addCheckbox("Results", "Med resultat", 0, withResult); gdi.addCheckbox("Results", "Med resultat", 0, withResult);*/
} }
else if (setupPrinter) {
gdi.dropLine(0.2);
gdi.addButton("PrinterSetup", "Skrivare...", ListsCB, "Skrivarinställningar");
}
gdi.popX(); gdi.popX();
gdi.fillDown();
RECT rc;
rc.top = gdi.getCY();
rc.left = gdi.getCX();
gdi.setCX(gdi.getCX() + gdi.scaleLength(8));
gdi.dropLine();
const char *ctype = type == Splits ? "SPExtra" : "EntryExtra"; const char *ctype = type == Splits ? "SPExtra" : "EntryExtra";
customTextLines(oe, ctype, gdi); customTextLines(oe, ctype, !oe.empty(), gdi);
gdi.dropLine();
rc.right = gdi.getWidth();
rc.bottom = gdi.getCY();
gdi.addRectangle(rc, colorLightCyan);
if (type == Splits) { if (type == Splits) {
gdi.dropLine(1.5);
const bool wideFormat = oe.getPropertyInt("WideSplitFormat", 0) == 1; const bool wideFormat = oe.getPropertyInt("WideSplitFormat", 0) == 1;
gdi.addCheckbox("WideFormat", "Sträcktider i kolumner (för standardpapper)", ListsCB, wideFormat); gdi.addCheckbox("WideFormat", "Sträcktider i kolumner (för standardpapper)", ListsCB, wideFormat);
@ -2769,7 +2843,7 @@ void TabList::splitPrintSettings(oEvent &oe, gdioutput &gdi, bool setupPrinter,
} }
} }
gdi.dropLine();
gdi.fillRight(); gdi.fillRight();
gdi.setData("Type", ctype); gdi.setData("Type", ctype);
gdi.addButton("SavePS", "OK", ListsCB).setDefault().setExtra(returnMode); gdi.addButton("SavePS", "OK", ListsCB).setDefault().setExtra(returnMode);
@ -2791,11 +2865,12 @@ void TabList::saveExtraLines(oEvent &oe, const char *dataField, gdioutput &gdi)
oe.setExtraLines(dataField, lines); oe.setExtraLines(dataField, lines);
} }
void TabList::customTextLines(oEvent &oe, const char *dataField, gdioutput &gdi) { void TabList::customTextLines(oEvent &oe, const char *dataField, bool withSymbols, gdioutput &gdi) {
gdi.dropLine(2.5);
gdi.addString("", fontMediumPlus, "Egna textrader"); gdi.addString("", fontMediumPlus, "Egna textrader");
gdi.dropLine(0.3); if (withSymbols) {
gdi.addString("", 10, "help:custom_text_lines"); gdi.dropLine(0.3);
gdi.addString("", 10, "help:custom_text_lines");
}
gdi.dropLine(0.8); gdi.dropLine(0.8);
int yp = gdi.getCY(); int yp = gdi.getCY();
@ -2825,18 +2900,20 @@ void TabList::customTextLines(oEvent &oe, const char *dataField, gdioutput &gdi)
gdi.fillDown(); gdi.fillDown();
gdi.dropLine(2); gdi.dropLine(2);
} }
gdi.pushX(); if (withSymbols) {
gdi.pushY(); gdi.pushX();
gdi.pushY();
gdi.setCX(xp); gdi.setCX(xp);
gdi.setCY(yp); gdi.setCY(yp);
gdi.addListBox("Symbols", 500, 160); gdi.addListBox("Symbols", 500, 160);
gdi.setTabStops("Symbols", 300); gdi.setTabStops("Symbols", 300);
vector < pair<wstring, size_t>> symb; vector < pair<wstring, size_t>> symb;
MetaList::fillSymbols(symb); MetaList::fillSymbols(symb);
gdi.addItem("Symbols", symb); gdi.addItem("Symbols", symb);
gdi.popX(); gdi.popX();
gdi.popY(); gdi.popY();
}
} }
void TabList::liveResult(gdioutput &gdi, oListInfo &li) { void TabList::liveResult(gdioutput &gdi, oListInfo &li) {

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -147,7 +147,7 @@ public:
}; };
static void splitPrintSettings(oEvent &oe, gdioutput &gdi, bool setupPrinter, TabType returnMode, PrintSettingsSelection type); static void splitPrintSettings(oEvent &oe, gdioutput &gdi, bool setupPrinter, TabType returnMode, PrintSettingsSelection type);
static void customTextLines(oEvent &oe, const char *dataField, gdioutput &gdi); static void customTextLines(oEvent &oe, const char *dataField, bool withSymbols, gdioutput &gdi);
static void saveExtraLines(oEvent &oe, const char *dataField, gdioutput &gdi); static void saveExtraLines(oEvent &oe, const char *dataField, gdioutput &gdi);
static void enableWideFormat(gdioutput &gdi, bool wide); static void enableWideFormat(gdioutput &gdi, bool wide);

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -28,6 +28,9 @@
#include <commdlg.h> #include <commdlg.h>
#include "oEvent.h" #include "oEvent.h"
#include "metalist.h"
#include "generalresult.h"
#include "xmlparser.h" #include "xmlparser.h"
#include "gdioutput.h" #include "gdioutput.h"
#include "gdiconstants.h" #include "gdiconstants.h"
@ -197,7 +200,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) {
gdi.setText("Team", L""); gdi.setText("Team", L"");
} }
gdi.setText("TimeAdjust", getTimeMS(r->getTimeAdjustment())); gdi.setText("TimeAdjust", formatTimeMS(r->getTimeAdjustment(false), false));
gdi.setText("PointAdjust", -r->getPointAdjustment()); gdi.setText("PointAdjust", -r->getPointAdjustment());
#ifdef _DEBUG #ifdef _DEBUG
@ -221,13 +224,13 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) {
out += L" (" + itow(placeAcc[k]) + L")"; out += L" (" + itow(placeAcc[k]) + L")";
if (after[k] > 0) if (after[k] > 0)
out += L" +" + getTimeMS(after[k]); out += L" +" + formatTimeMS(after[k], false);
if (k < afterAcc.size() && afterAcc[k]>0) if (k < afterAcc.size() && afterAcc[k]>0)
out += L" (+" + getTimeMS(afterAcc[k]) + L")"; out += L" (+" + formatTimeMS(afterAcc[k], false) + L")";
if (delta[k] > 0) if (delta[k] > 0)
out += L" B: " + getTimeMS(delta[k]); out += L" B: " + formatTimeMS(delta[k], false);
out += L" | "; out += L" | ";
@ -528,7 +531,7 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) {
else else
classId = lbi.data; classId = lbi.data;
int year = 0; const wstring year;
pRunner r; pRunner r;
bool cardNoChanged = false; bool cardNoChanged = false;
if (runnerId==0) { if (runnerId==0) {
@ -926,11 +929,12 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
if (!r) return 0; if (!r) return 0;
gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter); gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter);
if (bi.getExtraInt() == 0) if (bi.getExtraInt() == 0)
r->printSplits(gdiprint); r->printSplits(gdiprint);
else else
r->printStartInfo(gdiprint); r->printStartInfo(gdiprint);
gdiprint.print(oe, 0, false, true);
gdiprint.print(oe, nullptr, false, true);
gdiprint.fetchPrinterSettings(splitPrinter); gdiprint.fetchPrinterSettings(splitPrinter);
} }
else if (bi.id == "PrintSettings") { else if (bi.id == "PrintSettings") {
@ -961,7 +965,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
pRunner r = oe->getRunner(runnerId, 0); pRunner r = oe->getRunner(runnerId, 0);
if (!name.empty() && r && r->getName() != name && r->getNameRaw() != name) { if (!name.empty() && r && r->getName() != name && r->getNameRaw() != name) {
if (gdi.ask(L"Vill du lägga till deltagaren 'X'?#" + name)) { if (gdi.ask(L"Vill du lägga till deltagaren 'X'?#" + name)) {
r = oe->addRunner(name, 0, 0, 0,0, false); r = oe->addRunner(name, 0, 0, 0, L"", false);
runnerId = r->getId(); runnerId = r->getId();
} }
save(gdi, runnerId, false); save(gdi, runnerId, false);
@ -973,7 +977,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
ListBoxInfo lbi; ListBoxInfo lbi;
gdi.getSelectedItem("RClass", lbi); gdi.getSelectedItem("RClass", lbi);
pRunner r = oe->addRunner(oe->getAutoRunnerName(), 0,0,0,0, false); pRunner r = oe->addRunner(oe->getAutoRunnerName(), 0, 0, 0, L"", false);
int clsId = lbi.data; int clsId = lbi.data;
if (clsId > 0) { if (clsId > 0) {
pClass tCls = oe->getClass(clsId); pClass tCls = oe->getClass(clsId);
@ -1095,7 +1099,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
r->synchronize(); r->synchronize();
card->fillPunches(gdi, "Punches", r->getCourse(true)); card->fillPunches(gdi, "Punches", r->getCourse(true));
gdi.setText("Time", r->getRunningTimeS(true)); gdi.setText("Time", r->getRunningTimeS(true, SubSecond::Auto));
gdi.selectItemByData("Status", r->getStatus()); gdi.selectItemByData("Status", r->getStatus());
} }
else if (bi.id=="Check") { else if (bi.id=="Check") {
@ -1123,8 +1127,46 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
gdi.clearAutoComplete(ii.id); gdi.clearAutoComplete(ii.id);
} }
} }
else if (ii.id == "CardNo") {
bool show = false;
if (ii.text.length() > 0) {
vector<AutoCompleteRecord> records;
getAutoCompleteUnpairedCards(gdi, ii.text, records);
if (!records.empty()) {
auto& ac = gdi.addAutoComplete(ii.id);
ac.setAutoCompleteHandler(this);
ac.setData(records);
ac.show();
show = true;
}
}
if (!show) {
gdi.clearAutoComplete(ii.id);
}
}
} }
else if (type==GUI_INPUT) { else if (type == GUI_FOCUS) {
InputInfo ii = *(InputInfo*)data;
if (ii.id == "CardNo") {
pRunner r = runnerId > 0 ? oe->getRunner(runnerId, 0) : nullptr;
if (r && r->getCard() == nullptr) {
bool show = false;
vector<AutoCompleteRecord> records;
getAutoCompleteUnpairedCards(gdi, L"", records);
if (!records.empty()) {
auto& ac = gdi.addAutoComplete(ii.id);
ac.setAutoCompleteHandler(this);
ac.setData(records);
ac.show();
show = true;
}
if (!show) {
gdi.clearAutoComplete(ii.id);
}
}
}
}
else if (type == GUI_INPUT) {
InputInfo ii=*(InputInfo *)data; InputInfo ii=*(InputInfo *)data;
if (ii.id=="CardNo") { if (ii.id=="CardNo") {
@ -1311,7 +1353,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
ac.setAutoCompleteHandler(this); ac.setAutoCompleteHandler(this);
vector<AutoCompleteRecord> items; vector<AutoCompleteRecord> items;
for (auto club : clubs) for (auto club : clubs)
items.emplace_back(club->getDisplayName(), club->getName(), club->getId()); items.emplace_back(club->getDisplayName(), -int(items.size()), club->getName(), club->getId());
ac.setData(items); ac.setData(items);
ac.show(); ac.show();
@ -1650,12 +1692,12 @@ void TabRunner::showRunnerReport(gdioutput &gdi) {
} }
wstring tInfo = t->getName(); wstring tInfo = t->getName();
if (t->statusOK(true)) { if (t->statusOK(true, true)) {
tInfo += L", " + t->getRunningTimeS(true) + lang.tl(", Placering: ") + t->getPlaceS(); tInfo += L", " + t->getRunningTimeS(true, SubSecond::Auto) + lang.tl(", Placering: ") + t->getPlaceS();
if (t->getTimeAfter(-1) > 0) if (t->getTimeAfter(-1, true) > 0)
tInfo += L", +" + formatTime(t->getTimeAfter(-1)); tInfo += L", +" + formatTime(t->getTimeAfter(-1, true));
} }
else if (t->getStatusComputed() != StatusUnknown) { else if (t->getStatusComputed(true) != StatusUnknown) {
tInfo += L" " + t->getStatusS(true, true); tInfo += L" " + t->getStatusS(true, true);
} }
@ -1719,7 +1761,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
gdi.dropLine(0.3); gdi.dropLine(0.3);
if (r->statusOK(true)) { if (r->statusOK(true, true)) {
int total, finished, dns; int total, finished, dns;
r->getClassRef(true)->getNumResults(r->getLegNumber(), total, finished, dns); r->getClassRef(true)->getNumResults(r->getLegNumber(), total, finished, dns);
@ -1736,7 +1778,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
} }
} }
} }
else if (r->getStatusComputed() != StatusUnknown) { else if (r->getStatusComputed(true) != StatusUnknown) {
gdi.addStringUT(fontMediumPlus, str).setColor(colorRed); gdi.addStringUT(fontMediumPlus, str).setColor(colorRed);
} }
@ -1747,7 +1789,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
gdi.addString("", fontMedium, L"Starttid: X #" + r->getStartTimeCompact()); gdi.addString("", fontMedium, L"Starttid: X #" + r->getStartTimeCompact());
if (r->getFinishTime() > 0) if (r->getFinishTime() > 0)
gdi.addString("", fontMedium, L"Måltid: X #" + r->getFinishTimeS()); gdi.addString("", fontMedium, L"Måltid: X #" + r->getFinishTimeS(false, SubSecond::Auto));
const wstring &after = oe.formatListString(lRunnerTimeAfter, r); const wstring &after = oe.formatListString(lRunnerTimeAfter, r);
if (!after.empty()) { if (!after.empty()) {
@ -1811,7 +1853,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
GDICOLOR color = colorDefault; GDICOLOR color = colorDefault;
if (k < int(after.size()) ) { if (k < int(after.size()) ) {
if (after[k] > 0) if (after[k] > 0)
split += L" (" + itow(place[k]) + L", +" + getTimeMS(after[k]) + L")"; split += L" (" + itow(place[k]) + L", +" + formatTimeMS(after[k], false) + L")";
else if (place[k] == 1) else if (place[k] == 1)
split += lang.tl(" (sträckseger)"); split += lang.tl(" (sträckseger)");
else if (place[k] > 0) else if (place[k] > 0)
@ -1823,11 +1865,11 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
gdi.addStringUT(yp + lh, cx, fontMedium, split, limit); gdi.addStringUT(yp + lh, cx, fontMedium, split, limit);
if (k>0 && k < int(placeAcc.size())) { if (k>0 && k < int(placeAcc.size())) {
split = r->getPunchTimeS(k, false); split = r->getPunchTimeS(k, false, false, SubSecond::Auto);
wstring pl = placeAcc[k] > 0 ? itow(placeAcc[k]) : L"-"; wstring pl = placeAcc[k] > 0 ? itow(placeAcc[k]) : L"-";
if (k < int(afterAcc.size()) ) { if (k < int(afterAcc.size()) ) {
if (afterAcc[k] > 0) if (afterAcc[k] > 0)
split += L" (" + pl + L", +" + getTimeMS(afterAcc[k]) + L")"; split += L" (" + pl + L", +" + formatTimeMS(afterAcc[k], false) + L")";
else if (placeAcc[k] == 1) else if (placeAcc[k] == 1)
split += lang.tl(" (ledare)"); split += lang.tl(" (ledare)");
else if (placeAcc[k] > 0) else if (placeAcc[k] > 0)
@ -1837,7 +1879,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
} }
if (k < int(delta.size()) && delta[k] > 0 ) { if (k < int(delta.size()) && delta[k] > 0 ) {
gdi.addString("", yp + 3*lh, cx, fontMedium, L"Bomtid: X#" + getTimeMS(delta[k])); gdi.addString("", yp + 3*lh, cx, fontMedium, L"Bomtid: X#" + formatTimeMS(delta[k], false, SubSecond::Off));
color = (delta[k] > bestTime * 0.5 && delta[k]>60 ) ? color = (delta[k] > bestTime * 0.5 && delta[k]>60 ) ?
colorMediumDarkRed : colorMediumRed; colorMediumDarkRed : colorMediumRed;
@ -1897,7 +1939,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
if (st > 0 && t > st) { if (st > 0 && t > st) {
wstring split = formatTimeHMS(t-st); wstring split = formatTimeHMS(t-st);
if (lastT>0 && st != lastT && lastT < t) if (lastT>0 && st != lastT && lastT < t)
split += L" (" + getTimeMS(t-lastT) + L")"; split += L" (" + formatTimeMS(t-lastT, false) + L")";
gdi.addStringUT(yp + 2*lh, cx, normalText, split, limit); gdi.addStringUT(yp + 2*lh, cx, normalText, split, limit);
} }
} }
@ -2267,8 +2309,8 @@ void TabRunner::updateStatus(gdioutput &gdi, pRunner r) {
if (!r) return; if (!r) return;
gdi.setText("Start", r->getStartTimeS()); gdi.setText("Start", r->getStartTimeS());
gdi.setText("Finish", r->getFinishTimeS()); gdi.setText("Finish", r->getFinishTimeS(false, SubSecond::Auto));
gdi.setText("Time", r->getRunningTimeS(false)); gdi.setText("Time", r->getRunningTimeS(false, SubSecond::Auto));
gdi.setText("Points", itow(r->getRogainingPoints(false, false))); gdi.setText("Points", itow(r->getRogainingPoints(false, false)));
gdi.selectItemByData("Status", r->getStatus()); gdi.selectItemByData("Status", r->getStatus());
@ -2321,7 +2363,7 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) {
wstring ptime; wstring ptime;
if (punch->getTimeInt() > 0) if (punch->getTimeInt() > 0)
ptime = punch->getTime(); ptime = punch->getTime(false, SubSecond::Auto);
gdi.setText("PTime", ptime); gdi.setText("PTime", ptime);
@ -2386,7 +2428,7 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) {
if (!oc) return 0; if (!oc) return 0;
vector<int> nmp; vector<int> nmp;
if (oc->getStatus() == oControl::StatusRogaining) { if (oc->getStatus() == oControl::ControlStatus::StatusRogaining || oc->getStatus() == oControl::ControlStatus::StatusRogainingRequired) {
r->evaluateCard(true, nmp, oc->getFirstNumber(), oBase::ChangeType::Update); //Add this punch r->evaluateCard(true, nmp, oc->getFirstNumber(), oBase::ChangeType::Update); //Add this punch
} }
else { else {
@ -2524,6 +2566,9 @@ bool TabRunner::loadPage(gdioutput &gdi)
gdi.pushX(); gdi.pushX();
gdi.dropLine(0.5); gdi.dropLine(0.5);
const int hy = gdi.getCY();
gdi.addString("", boldLarge, "Deltagare"); gdi.addString("", boldLarge, "Deltagare");
gdi.fillRight(); gdi.fillRight();
gdi.registerEvent("SearchRunner", runnerSearchCB).setKeyCommand(KC_FIND); gdi.registerEvent("SearchRunner", runnerSearchCB).setKeyCommand(KC_FIND);
@ -2552,7 +2597,7 @@ bool TabRunner::loadPage(gdioutput &gdi)
gdi.newColumn(); gdi.newColumn();
gdi.fillDown(); gdi.fillDown();
gdi.dropLine(1); gdi.setCY(hy);
gdi.fillRight(); gdi.fillRight();
gdi.pushX(); gdi.pushX();
gdi.addInput("Name", L"", 16, RunnerCB, L"Namn:"); gdi.addInput("Name", L"", 16, RunnerCB, L"Namn:");
@ -2736,11 +2781,11 @@ bool TabRunner::loadPage(gdioutput &gdi)
gdi.fillDown(); gdi.fillDown();
gdi.newColumn(); gdi.newColumn();
gdi.setCY(hy);
int hx = gdi.getCX(); int hx = gdi.getCX();
int hy = gdi.getCY();
gdi.setCX(hx + gdi.scaleLength(5)); gdi.setCX(hx + gdi.scaleLength(5));
gdi.dropLine(2.5); gdi.dropLine(2.0);
gdi.addListBox("Punches", 150, 300, PunchesCB, L"Stämplingar:").ignore(true); gdi.addListBox("Punches", 150, 300, PunchesCB, L"Stämplingar:").ignore(true);
gdi.addButton("RemoveC", "Ta bort stämpling >>", RunnerCB); gdi.addButton("RemoveC", "Ta bort stämpling >>", RunnerCB);
@ -2755,7 +2800,8 @@ bool TabRunner::loadPage(gdioutput &gdi)
int contY = gdi.getCY(); int contY = gdi.getCY();
gdi.newColumn(); gdi.newColumn();
gdi.dropLine(2.5); gdi.setCY(hy);
gdi.dropLine(2.0);
gdi.fillDown(); gdi.fillDown();
gdi.addListBox("Course", 140, 300, PunchesCB, L"Banmall:").ignore(true); gdi.addListBox("Course", 140, 300, PunchesCB, L"Banmall:").ignore(true);
gdi.addButton("AddC", "<< Lägg till stämpling", PunchesCB); gdi.addButton("AddC", "<< Lägg till stämpling", PunchesCB);
@ -3136,7 +3182,7 @@ void TabRunner::loadEconomy(gdioutput &gdi, oRunner &r) {
void TabRunner::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) { void TabRunner::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) {
auto bi = gdi.setText(info.getTarget(), info.getCurrent().c_str()); auto bi = gdi.setText(info.getTarget(), info.getCurrent().c_str());
if (bi) { if (bi->id == "Name") {
int ix = info.getCurrentInt(); int ix = info.getCurrentInt();
bi->setExtra(ix); bi->setExtra(ix);
if (info.getTarget() == "Name") { if (info.getTarget() == "Name") {
@ -3153,6 +3199,10 @@ void TabRunner::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) {
} }
} }
} }
else if (bi->id == "CardNo") {
}
gdi.clearAutoComplete(""); gdi.clearAutoComplete("");
gdi.TabFocus(1); gdi.TabFocus(1);
} }
@ -3173,3 +3223,81 @@ pClub TabRunner::extractClub(oEvent *oe, gdioutput &gdi) {
} }
return dbClub; return dbClub;
} }
void TabRunner::getAutoCompleteUnpairedCards(gdioutput & gdi,
const wstring& w,
vector<AutoCompleteRecord>& records) {
pCourse matchCourse = nullptr;
ListBoxInfo lbi;
auto cls = gdi.getSelectedItem("RClass");
if (runnerId) {
pRunner r = oe->getRunner(runnerId, 0);
if (r && r->getClassId(true) == cls.first)
matchCourse = r->getCourse(false);
}
if (!matchCourse && cls.first) {
pClass cPtr = oe->getClass(cls.first);
matchCourse = cPtr ? cPtr->getCourse(true) : nullptr;
}
records.clear();
int nr = _wtoi(w.c_str());
char sIn[32];
char sComp[32];
vector<pCard> cards;
oe->getCards(cards, false, true);
if (w.empty())
sIn[0] = 0;
else
sprintf_s(sIn, "%d", nr);
vector<pair<int, pCard>> matchedCards;
for (pCard c : cards) {
sprintf_s(sComp, "%d", c->getCardNo());
bool match = true;
int i = 0;
while (sIn[i]) {
if (sComp[i] != sIn[i]) {
match = false;
break;
}
i++;
}
if (match) {
int score = 0;
if (matchCourse) {
int d = matchCourse->distance(*c);
if (d < 0)
d = min(10 - d, 20);
else
d = max(d, 10);
int age = c->getAge();
if (age > 10000)
age = 10000;
if (age < 0)
age = 0;
score = d * 10000 + age;
matchedCards.emplace_back(score, c);
}
}
}
sort(matchedCards.begin(), matchedCards.end());
wstring star;
for (auto &mc : matchedCards) {
wstring m = mc.second->getModified().getUpdateTime();
const wstring& cno = mc.second->getCardNoString();
star = mc.first < 3 * 10000 ? L"*" : L""; // Star if contents matches course
records.emplace_back(cno + star + L", "
+ lang.tl("X stämplingar#" + itos(mc.second->getNumControlPunches(-1, -1)))
+ L" (" + m + L")", -int(records.size()), cno, mc.second->getCardNo());
}
}

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -25,6 +25,7 @@
#include "autocompletehandler.h" #include "autocompletehandler.h"
class Table; class Table;
struct AutoCompleteRecord;
class TabRunner : class TabRunner :
public TabBase, AutoCompleteHandler public TabBase, AutoCompleteHandler
@ -107,6 +108,8 @@ private:
shared_ptr<EconomyHandler> ecoHandler; shared_ptr<EconomyHandler> ecoHandler;
EconomyHandler *getEconomyHandler(oRunner &r); EconomyHandler *getEconomyHandler(oRunner &r);
void getAutoCompleteUnpairedCards(gdioutput &gdi, const wstring& w, vector<AutoCompleteRecord>& records);
protected: protected:
void clearCompetitionData(); void clearCompetitionData();
public: public:

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -30,9 +30,9 @@ struct PunchInfo;
class csvparser; class csvparser;
struct AutoCompleteRecord; struct AutoCompleteRecord;
class TabSI : public TabBase, AutoCompleteHandler { class TabSI final : public TabBase, AutoCompleteHandler {
public: public:
enum SIMode { enum class SIMode {
ModeReadOut, ModeReadOut,
ModeAssignCards, ModeAssignCards,
ModeCheckCards, ModeCheckCards,
@ -40,9 +40,15 @@ public:
ModeCardData, ModeCardData,
ModeRegisterCards, ModeRegisterCards,
}; };
map<SIMode, string> modeName;
void setMode(SIMode m) { mode = m; } void setMode(SIMode m) { mode = m; }
private: private:
/** Try to automatcally assign a class to runner (if none is given) /** Try to automatcally assign a class to runner (if none is given)
Return true if runner has a class on exist */ Return true if runner has a class on exist */
bool autoAssignClass(pRunner r, const SICard &sic); bool autoAssignClass(pRunner r, const SICard &sic);
@ -57,13 +63,13 @@ private:
bool silent=false); bool silent=false);
bool processUnmatched(gdioutput &gdi, const SICard &csic, bool silent); bool processUnmatched(gdioutput &gdi, const SICard &csic, bool silent);
void rentCardInfo(gdioutput &gdi, int width);
bool interactiveReadout; bool interactiveReadout;
bool useDatabase; bool useDatabase;
bool printSplits; bool printSplits;
bool printStartInfo; bool printStartInfo;
bool manualInput; bool manualInput;
bool multipleStarts = false;
PrinterObject splitPrinter; PrinterObject splitPrinter;
list< pair<unsigned, int> > printPunchRunnerIdQueue; list< pair<unsigned, int> > printPunchRunnerIdQueue;
void addToPrintQueue(pRunner r); void addToPrintQueue(pRunner r);
@ -97,10 +103,9 @@ private:
//Operation mode //Operation mode
SIMode mode; SIMode mode;
bool lockedFunction = false; bool lockedFunction = false;
bool allowControl = true;
bool allowFinish = true;
bool allowStart = false;
void changeMapping(gdioutput& gdi) const;
void fillMappings(gdioutput& gdi) const;
int currentAssignIndex; int currentAssignIndex;
void printSIInfo(gdioutput &gdi, const wstring &port) const; void printSIInfo(gdioutput &gdi, const wstring &port) const;
@ -219,10 +224,47 @@ private:
int readoutFunctionX = 0; int readoutFunctionX = 0;
int readoutFunctionY = 0; int readoutFunctionY = 0;
int optionBarPosY = 0;
int optionBarPosX = 0;
int check_toolbar_xb = 0;
int check_toolbar_yb = 0;
enum class CheckBox {
Interactive,
UseDB,
PrintSplits,
PrintStart,
Manual,
SeveralTurns,
AutoTie,
AutoTieRent
};
void checkBoxToolBar(gdioutput& gdi, const set<CheckBox> &items) const;
void playSoundResource(int res) const; void playSoundResource(int res) const;
void playSoundFile(const wstring& file) const; void playSoundFile(const wstring& file) const;
struct StoredReadout {
wstring info;
wstring warnings;
wstring cardno;
wstring statusline;
vector<int> MP;
GDICOLOR color;
bool rentCard = false;
RECT computeRC(gdioutput &gdi) const;
void render(gdioutput &gdi, const RECT &rc) const;
static void rentCardInfo(gdioutput &gdi, const RECT &rcIn);
};
list<StoredReadout> readCards;
void renderReadCard(gdioutput &gdi, int maxNumber);
protected: protected:
void clearCompetitionData(); void clearCompetitionData() final;
static wstring getPlace(const oRunner *r); static wstring getPlace(const oRunner *r);
static wstring getTimeString(const oRunner *r); static wstring getTimeString(const oRunner *r);
@ -271,6 +313,8 @@ public:
int siCB(gdioutput &gdi, int type, void *data); int siCB(gdioutput &gdi, int type, void *data);
void writeDefaultHiredCards();
void logCard(gdioutput &gdi, const SICard &card); void logCard(gdioutput &gdi, const SICard &card);
void setCardNumberField(const string &fieldId) {insertCardNumberField=fieldId;} void setCardNumberField(const string &fieldId) {insertCardNumberField=fieldId;}
@ -286,7 +330,7 @@ public:
void clearQueue() { CardQueue.clear(); } void clearQueue() { CardQueue.clear(); }
void refillComPorts(gdioutput &gdi); void refillComPorts(gdioutput &gdi);
bool loadPage(gdioutput &gdi); bool loadPage(gdioutput &gdi) final;
void showReadoutMode(gdioutput & gdi); void showReadoutMode(gdioutput & gdi);
void showReadoutStatus(gdioutput &gdi, const oRunner *r, void showReadoutStatus(gdioutput &gdi, const oRunner *r,

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -176,7 +176,7 @@ int TabSpeaker::processButton(gdioutput &gdi, const ButtonInfo &bu)
gdi.fillDown(); gdi.fillDown();
vector< pair<wstring, size_t> > d; vector< pair<wstring, size_t> > d;
oe->fillControls(d, oEvent::CTCourseControl); oe->fillControls(d, oEvent::ControlType::CourseControl);
gdi.addItem("Controls", d); gdi.addItem("Controls", d);
gdi.setSelection("Controls", controlsToWatch); gdi.setSelection("Controls", controlsToWatch);
@ -382,7 +382,7 @@ int TabSpeaker::processButton(gdioutput &gdi, const ButtonInfo &bu)
controlsToWatch.insert(-2); // Non empty but no control controlsToWatch.insert(-2); // Non empty but no control
for (set<int>::iterator it=controlsToWatch.begin();it!=controlsToWatch.end();++it) { for (set<int>::iterator it=controlsToWatch.begin();it!=controlsToWatch.end();++it) {
pControl pc=oe->getControl(*it, false); pControl pc=oe->getControl(*it, false, false);
if (pc) { if (pc) {
pc->setRadio(true); pc->setRadio(true);
pc->synchronize(true); pc->synchronize(true);
@ -722,7 +722,7 @@ void TabSpeaker::splitAnalysis(gdioutput &gdi, int xp, int yp, pRunner r)
else else
first = false; first = false;
timeloss += pc->getControlOrdinal(j) + L". " + formatTime(delta[j]); timeloss += pc->getControlOrdinal(j) + L". " + formatTime(delta[j], SubSecond::Auto);
} }
if (timeloss.length() > charlimit || (!timeloss.empty() && !first && j+1 == delta.size())) { if (timeloss.length() > charlimit || (!timeloss.empty() && !first && j+1 == delta.size())) {
gdi.addStringUT(yp, xp, 0, timeloss).setColor(colorDarkRed); gdi.addStringUT(yp, xp, 0, timeloss).setColor(colorDarkRed);
@ -1223,7 +1223,7 @@ void TabSpeaker::storeManualTime(gdioutput &gdi)
throw std::exception(bf); throw std::exception(bf);
} }
oe->addFreePunch(itime, punch, sino, true); oe->addFreePunch(itime, punch, 0, sino, true);
gdi.restore("manual", false); gdi.restore("manual", false);
gdi.addString("", 0, L"Löpare: X, kontroll: Y, kl Z#" + Name + L"#" + oPunch::getType(punch) + L"#" + oe->getAbsTime(itime)); gdi.addString("", 0, L"Löpare: X, kontroll: Y, kl Z#" + Name + L"#" + oPunch::getType(punch) + L"#" + oe->getAbsTime(itime));
@ -1432,7 +1432,7 @@ void TabSpeaker::loadSettings(vector< multimap<string, wstring> > &settings) {
xmlList allS; xmlList allS;
s.getObjects(allS); s.getObjects(allS);
for (auto &prop : allS) { for (auto &prop : allS) {
settings.back().insert(make_pair(prop.getName(), prop.getw())); settings.back().insert(make_pair(prop.getName(), prop.getWStr()));
} }
} }
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -274,9 +274,9 @@ void TabTeam::updateTeamStatus(gdioutput &gdi, pTeam t)
} }
gdi.setText("Start", t->getStartTimeS()); gdi.setText("Start", t->getStartTimeS());
gdi.setText("Finish",t->getFinishTimeS()); gdi.setText("Finish",t->getFinishTimeS(false, SubSecond::Auto));
gdi.setText("Time", t->getRunningTimeS(true)); gdi.setText("Time", t->getRunningTimeS(true, SubSecond::Auto));
gdi.setText("TimeAdjust", getTimeMS(t->getTimeAdjustment())); gdi.setText("TimeAdjust", formatTimeMS(t->getTimeAdjustment(false), false, SubSecond::Auto));
gdi.setText("PointAdjust", -t->getPointAdjustment()); gdi.setText("PointAdjust", -t->getPointAdjustment());
gdi.selectItemByData("Status", t->getStatus()); gdi.selectItemByData("Status", t->getStatus());
@ -474,7 +474,7 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) {
} }
} }
else else
r = oe->addRunner(name, t->getClubId(), t->getClassId(false), cardNo, 0, false); r = oe->addRunner(name, t->getClubId(), t->getClassId(false), cardNo, L"", false);
r->setName(name, true); r->setName(name, true);
r->setCardNo(cardNo, true); r->setCardNo(cardNo, true);
@ -659,7 +659,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
bool rent = gdi.isChecked("DirRent"); bool rent = gdi.isChecked("DirRent");
if (r == 0) { if (r == 0) {
r = oe->addRunner(name, clb ? clb->getId() : t->getClubId(), t->getClassId(false), card, 0, false); r = oe->addRunner(name, clb ? clb->getId() : t->getClubId(), t->getClassId(false), card, L"", false);
} }
if (rent) if (rent)
r->getDI().setInt("CardFee", oe->getBaseCardFee()); r->getDI().setInt("CardFee", oe->getBaseCardFee());
@ -728,7 +728,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
pTeam t = oe->getTeam(teamId); pTeam t = oe->getTeam(teamId);
if (!t || !t->getClassRef(false)) if (!t || !t->getClassRef(false))
return 0; return 0;
t->synchronize();
pClass pc = t->getClassRef(false); pClass pc = t->getClassRef(false);
int nf = pc->getNumForks(); int nf = pc->getNumForks();
ListBoxInfo lbi; ListBoxInfo lbi;
@ -745,7 +745,13 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
continue; continue;
int currentKey = max(newSno-1, 0) % nf; int currentKey = max(newSno-1, 0) % nf;
if (currentKey == lbi.data) { if (currentKey == lbi.data) {
for (int j = 0; j < t->getNumRunners(); j++) {
if (t->getRunner(j)) {
t->getRunner(j)->setCourseId(0);
t->synchronize(true);
}
}
t->setStartNo(newSno, oBase::ChangeType::Update); t->setStartNo(newSno, oBase::ChangeType::Update);
t->synchronize(true); t->synchronize(true);
t->evaluate(oBase::ChangeType::Update); t->evaluate(oBase::ChangeType::Update);
@ -1163,6 +1169,9 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
} }
break; break;
} }
else if (ii.id == "R" + itos(i)) {
enableRunner(gdi, i, !ii.text.empty());
}
} }
} }
@ -1177,7 +1186,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
ac.setAutoCompleteHandler(this); ac.setAutoCompleteHandler(this);
vector<AutoCompleteRecord> items; vector<AutoCompleteRecord> items;
for (auto club : clubs) for (auto club : clubs)
items.emplace_back(club->getDisplayName(), club->getName(), club->getId()); items.emplace_back(club->getDisplayName(), -int(items.size()), club->getName(), club->getId());
ac.setData(items); ac.setData(items);
ac.show(); ac.show();
@ -1228,32 +1237,37 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t)
int yp = gdi.getCY(); int yp = gdi.getCY();
int numberPos = xp; int numberPos = xp;
xp += gdi.scaleLength(25); xp += gdi.scaleLength(25);
int dx[6] = {0, 184, 220, 290, 316, 364}; const int dxIn[6] = {0, 184, 220, 300, 326, 374};
dx[1] = gdi.getInputDimension(18).first + gdi.scaleLength(4); int dx[6];
for (int i = 2; i<6; i++) dx[0] = 0;
dx[i] = dx[1] + gdi.scaleLength(dx[i]-188);
for (int i = 1; i < 6; i++) {
if (i == 1)
dx[i] = gdi.getInputDimension(18).first + gdi.scaleLength(4);
else if (i == 3)
dx[i] = dx[i-1] + gdi.getInputDimension(7).first + gdi.scaleLength(4);
else
dx[i] = dx[i-1] + gdi.scaleLength(dxIn[i] - dxIn[i-1]);
}
gdi.addString("", yp, xp + dx[0], 0, "Namn:"); gdi.addString("", yp, xp + dx[0], 0, "Namn:");
gdi.addString("", yp, xp + dx[2], 0, "Bricka:"); gdi.addString("", yp, xp + dx[2], 0, "Bricka:");
gdi.addString("", yp, xp + dx[3], 0, "Hyrd:"); gdi.addString("", yp, xp + dx[3]- gdi.scaleLength(5), 0, "Hyrd:");
gdi.addString("", yp, xp + dx[5], 0, "Status:"); gdi.addString("", yp, xp + dx[5], 0, "Status:");
gdi.dropLine(0.5); gdi.dropLine(0.5);
const int textOffY = gdi.scaleLength(4);
for (unsigned i = 0; i < pc->getNumStages(); i++) { for (unsigned i = 0; i < pc->getNumStages(); i++) {
yp = gdi.getCY() - gdi.scaleLength(3); yp = gdi.getCY() - gdi.scaleLength(3);
sprintf_s(bf, "R%d", i); sprintf_s(bf, "R%d", i);
gdi.pushX(); gdi.pushX();
bool hasSI = false; bool hasSI = false;
gdi.addStringUT(yp, numberPos, 0, pc->getLegNumber(i) + L"."); gdi.addStringUT(yp + textOffY, numberPos, 0, pc->getLegNumber(i) + L".");
if (pc->getLegRunner(i) == i) { if (pc->getLegRunner(i) == i) {
gdi.addInput(xp + dx[0], yp, bf, L"", 18, TeamCB);//Name gdi.addInput(xp + dx[0], yp, bf, L"", 18, TeamCB);//Name
gdi.addButton(xp + dx[1], yp - 2, gdi.scaleLength(28), "DR" + itos(i), "<>", TeamCB, "Knyt löpare till sträckan.", false, false); // Change gdi.addButton(xp + dx[1], yp - 2, gdi.scaleLength(28), "DR" + itos(i), "<>", TeamCB, "Knyt löpare till sträckan.", false, false); // Change
sprintf_s(bf_si, "SI%d", i); sprintf_s(bf_si, "SI%d", i);
hasSI = true; hasSI = true;
gdi.addInput(xp + dx[2], yp, bf_si, L"", 5, TeamCB).setExtra(i); //Si gdi.addInput(xp + dx[2], yp, bf_si, L"", 7, TeamCB).setExtra(i); //Si
gdi.addCheckbox(xp + dx[3], yp + gdi.scaleLength(10), "RENT" + itos(i), "", 0, false); //Rentcard gdi.addCheckbox(xp + dx[3], yp + gdi.scaleLength(10), "RENT" + itos(i), "", 0, false); //Rentcard
} }
else { else {
@ -1262,16 +1276,17 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t)
} }
gdi.addButton(xp + dx[4], yp - 2, gdi.scaleLength(38), "MR" + itos(i), "...", TeamCB, "Redigera deltagaren.", false, false); // Change gdi.addButton(xp + dx[4], yp - 2, gdi.scaleLength(38), "MR" + itos(i), "...", TeamCB, "Redigera deltagaren.", false, false); // Change
gdi.addString(("STATUS" + itos(i)).c_str(), yp + gdi.scaleLength(5), xp + dx[5], 0, "#MMMMMMMMMMMMMMMM"); gdi.addString(("STATUS" + itos(i)).c_str(), yp + textOffY, xp + dx[5], 0, "#MMMMMMMMMMMMMMMM");
gdi.setText("STATUS" + itos(i), L"", false); gdi.setText("STATUS" + itos(i), L"", false);
gdi.dropLine(0.5); gdi.dropLine(0.5);
gdi.popX(); gdi.popX();
if (t) { if (t) {
pRunner r = t->getRunner(i); pRunner r = t->getRunner(i);
enableRunner(gdi, i, r != nullptr);
if (r) { if (r) {
gdi.setText(bf, r->getNameRaw())->setExtra(r->getId()); gdi.setText(bf, r->getNameRaw())->setExtra(r->getId());
r->getPlace(true); // Ensure computed status is up-to-date
if (hasSI) { if (hasSI) {
int cno = r->getCardNo(); int cno = r->getCardNo();
gdi.setText(bf_si, cno > 0 ? itow(cno) : L""); gdi.setText(bf_si, cno > 0 ? itow(cno) : L"");
@ -1279,13 +1294,13 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t)
gdi.check("RENT" + itos(i), r->getDCI().getInt("CardFee") != 0); gdi.check("RENT" + itos(i), r->getDCI().getInt("CardFee") != 0);
} }
string sid = "STATUS" + itos(i); string sid = "STATUS" + itos(i);
if (r->statusOK(true)) { if (r->statusOK(true, true)) {
TextInfo * ti = (TextInfo *)gdi.setText(sid, L"OK, " + r->getRunningTimeS(true), false); TextInfo * ti = (TextInfo *)gdi.setText(sid, L"OK, " + r->getRunningTimeS(true, SubSecond::Auto), false);
if (ti) if (ti)
ti->setColor(colorGreen); ti->setColor(colorGreen);
} }
else if (r->getStatusComputed() != StatusUnknown) { else if (r->getStatusComputed(true) != StatusUnknown) {
TextInfo * ti = (TextInfo *)gdi.setText(sid, r->getStatusS(false, true) + L", " + r->getRunningTimeS(true), false); TextInfo * ti = (TextInfo *)gdi.setText(sid, r->getStatusS(false, true) + L", " + r->getRunningTimeS(true, SubSecond::Auto), false);
if (ti) if (ti)
ti->setColor(colorRed); ti->setColor(colorRed);
} }
@ -1334,6 +1349,15 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t)
gdi.refresh(); gdi.refresh();
} }
void TabTeam::enableRunner(gdioutput& gdi, int index, bool enable) {
string ix = itos(index);
string si = ("SI" + ix);
bool hasSI = enable || !gdi.getText(si, true).empty();
gdi.setInputStatus(si.c_str(), hasSI, true);
gdi.setInputStatus(("RENT" + ix).c_str(), hasSI, true);
gdi.setInputStatus(("MR" + ix).c_str(), enable, true);
}
bool TabTeam::loadPage(gdioutput &gdi, int id) { bool TabTeam::loadPage(gdioutput &gdi, int id) {
teamId = id; teamId = id;
return loadPage(gdi); return loadPage(gdi);
@ -1457,12 +1481,13 @@ bool TabTeam::loadPage(gdioutput &gdi)
gdi.popX(); gdi.popX();
gdi.selectItemByData("Status", 0); gdi.selectItemByData("Status", 0);
gdi.addString("TeamInfo", 0, "").setColor(colorRed);
gdi.dropLine(0.4); gdi.dropLine(0.4);
if (oe->hasAnyRestartTime()) { if (oe->hasAnyRestartTime()) {
gdi.addCheckbox("NoRestart", "Förhindra omstart", 0, false, "Förhindra att laget deltar i någon omstart"); gdi.addCheckbox("NoRestart", "Förhindra omstart", 0, false, "Förhindra att laget deltar i någon omstart");
} }
gdi.addString("TeamInfo", 0, " ").setColor(colorRed);
gdi.dropLine(1.5); gdi.dropLine(1.5);
const bool multiDay = oe->hasPrevStage(); const bool multiDay = oe->hasPrevStage();
@ -1694,7 +1719,7 @@ void TabTeam::saveTeamImport(gdioutput &gdi, bool useExisting) {
} }
} }
else { else {
r = oe->addRunner(member.name, member.club, 0, member.cardNo, 0, false); r = oe->addRunner(member.name, member.club, 0, member.cardNo, L"", false);
if (r && !member.course.empty()) { if (r && !member.course.empty()) {
pCourse pc = oe->getCourse(member.course); pCourse pc = oe->getCourse(member.course);
@ -1783,7 +1808,7 @@ void TabTeam::doAddTeamMembers(gdioutput &gdi) {
continue; continue;
pRunner r = 0; pRunner r = 0;
if (withFee) { if (withFee) {
r = oe->addRunner(nn, mt->getClubId(), 0, 0, 0, false); r = oe->addRunner(nn, mt->getClubId(), 0, 0, L"", false);
r->synchronize(); r->synchronize();
mt->setRunner(j, r, false); mt->setRunner(j, r, false);
r->addClassDefaultFee(true); r->addClassDefaultFee(true);
@ -1849,7 +1874,7 @@ void TabTeam::showRunners(gdioutput &gdi, const char *title,
void TabTeam::processChangeRunner(gdioutput &gdi, pTeam t, int leg, pRunner r) { void TabTeam::processChangeRunner(gdioutput &gdi, pTeam t, int leg, pRunner r) {
if (r && t && leg < t->getNumRunners()) { if (r && t && leg < t->getNumRunners()) {
pRunner oldR = t->getRunner(leg); pRunner oldR = t->getRunner(leg);
gdioutput::AskAnswer ans = gdioutput::AnswerNo; gdioutput::AskAnswer ans = gdioutput::AskAnswer::AnswerNo;
if (r == oldR) { if (r == oldR) {
gdi.restore("SelectR"); gdi.restore("SelectR");
return; return;
@ -1868,9 +1893,9 @@ void TabTeam::processChangeRunner(gdioutput &gdi, pTeam t, int leg, pRunner r) {
ans = gdi.askCancel(L"Vill du att X går in i laget?#" + r->getName()); ans = gdi.askCancel(L"Vill du att X går in i laget?#" + r->getName());
} }
if (ans == gdioutput::AnswerNo) if (ans == gdioutput::AskAnswer::AnswerNo)
return; return;
else if (ans == gdioutput::AnswerCancel) { else if (ans == gdioutput::AskAnswer::AnswerCancel) {
gdi.restore("SelectR"); gdi.restore("SelectR");
return; return;
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -74,6 +74,10 @@ private:
void switchRunners(pTeam team, int leg, pRunner r, pRunner oldR); void switchRunners(pTeam team, int leg, pRunner r, pRunner oldR);
/** Enable or disable edit for a team runner*/
void enableRunner(gdioutput &gdi, int index, bool enable);
protected: protected:
void clearCompetitionData(); void clearCompetitionData();

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -278,7 +278,8 @@ void Table::filter(int col, const wstring &filt, bool forceFilter)
sortIndex.resize(2); sortIndex.resize(2);
for (size_t k=2;k<baseIndex.size();k++) { for (size_t k=2;k<baseIndex.size();k++) {
if (filterMatchString(Data[baseIndex[k].index].cells[col].contents, filt_lc)) int score;
if (filterMatchString(Data[baseIndex[k].index].cells[col].contents, filt_lc, score))
sortIndex.push_back(baseIndex[k]); sortIndex.push_back(baseIndex[k]);
} }
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -415,6 +415,7 @@ struct TableSortIndex {
enum {TID_CLASSNAME, TID_COURSE, TID_NUM, TID_ID, TID_MODIFIED, enum {TID_CLASSNAME, TID_COURSE, TID_NUM, TID_ID, TID_MODIFIED,
TID_RUNNER, TID_CLUB, TID_START, TID_TIME, TID_RUNNER, TID_CLUB, TID_START, TID_TIME,
TID_FINISH, TID_STATUS, TID_RUNNINGTIME, TID_PLACE, TID_POINTS, TID_FINISH, TID_STATUS, TID_RUNNINGTIME, TID_PLACE, TID_POINTS,
TID_CARD, TID_TEAM, TID_LEG, TID_CONTROL, TID_CODES, TID_FEE, TID_PAID, TID_CARD, TID_TEAM, TID_LEG, TID_CONTROL, TID_UNIT, TID_CODES, TID_FEE, TID_PAID,
TID_INPUTTIME, TID_INPUTSTATUS, TID_INPUTPOINTS, TID_INPUTPLACE, TID_INPUTTIME, TID_INPUTSTATUS, TID_INPUTPOINTS, TID_INPUTPLACE,
TID_NAME, TID_NATIONAL, TID_SEX, TID_YEAR, TID_INDEX, TID_ENTER, TID_STARTNO, TID_VOLTAGE}; TID_NAME, TID_NATIONAL, TID_SEX, TID_YEAR, TID_INDEX,
TID_ENTER, TID_STARTNO, TID_VOLTAGE, TID_BATTERYDATE};

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -62,7 +62,7 @@ void TimeStamp::update()
__int64 &currenttime=*(__int64*)&ft; __int64 &currenttime=*(__int64*)&ft;
Time=unsigned((currenttime/10000000L) - minYearConstant*365*24*3600); Time=unsigned((currenttime/10000000L) - minYearConstant*365*24*timeConstSecPerHour);
} }
int TimeStamp::getAge() const int TimeStamp::getAge() const
@ -73,7 +73,7 @@ int TimeStamp::getAge() const
SystemTimeToFileTime(&st, &ft); SystemTimeToFileTime(&st, &ft);
__int64 &currenttime=*(__int64*)&ft; __int64 &currenttime=*(__int64*)&ft;
int CTime=int((currenttime/10000000)-minYearConstant*365*24*3600); int CTime=int((currenttime/10000000)-minYearConstant*365*24* timeConstSecPerHour);
return CTime-Time; return CTime-Time;
} }
@ -84,7 +84,7 @@ const string &TimeStamp::getStamp() const
return stampCode; return stampCode;
stampCodeTime = Time; stampCodeTime = Time;
__int64 ft64=(__int64(Time)+minYearConstant*365*24*3600)*10000000; __int64 ft64=(__int64(Time)+minYearConstant*365*24* timeConstSecPerHour)*10000000;
FILETIME &ft=*(FILETIME*)&ft64; FILETIME &ft=*(FILETIME*)&ft64;
SYSTEMTIME st; SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st); FileTimeToSystemTime(&ft, &st);
@ -105,9 +105,20 @@ const string &TimeStamp::getStamp(const string &sqlStampIn) const {
return stampCode; return stampCode;
} }
const wstring TimeStamp::getUpdateTime() const {
__int64 ft64 = (__int64(Time) + minYearConstant * 365 * 24 * timeConstSecPerHour) * 10000000;
FILETIME& ft = *(FILETIME*)&ft64;
SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st);
wchar_t bf[32];
swprintf_s(bf, L"%02d:%02d", st.wHour, st.wMinute);
return bf;
}
wstring TimeStamp::getStampString() const wstring TimeStamp::getStampString() const
{ {
__int64 ft64=(__int64(Time)+minYearConstant*365*24*3600)*10000000; __int64 ft64=(__int64(Time)+minYearConstant*365*24* timeConstSecPerHour)*10000000;
FILETIME &ft=*(FILETIME*)&ft64; FILETIME &ft=*(FILETIME*)&ft64;
SYSTEMTIME st; SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st); FileTimeToSystemTime(&ft, &st);
@ -120,7 +131,7 @@ wstring TimeStamp::getStampString() const
string TimeStamp::getStampStringN() const string TimeStamp::getStampStringN() const
{ {
__int64 ft64 = (__int64(Time) + minYearConstant * 365 * 24 * 3600) * 10000000; __int64 ft64 = (__int64(Time) + minYearConstant * 365 * 24 * timeConstSecPerHour) * 10000000;
FILETIME &ft = *(FILETIME*)&ft64; FILETIME &ft = *(FILETIME*)&ft64;
SYSTEMTIME st; SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st); FileTimeToSystemTime(&ft, &st);
@ -147,19 +158,41 @@ void TimeStamp::setStamp(const string &s)
SYSTEMTIME st; SYSTEMTIME st;
memset(&st, 0, sizeof(st)); memset(&st, 0, sizeof(st));
auto parse = [](const char* data, int size, const char*& next) -> int {
int ix = 0;
int out = 0;
while (ix < size && data[ix] >= '0' && data[ix] <= '9') {
out = (out << 1) + (out << 3) + data[ix] - '0';
ix++;
}
while (data[ix] && (data[ix] == ' ' || data[ix] == '-' || data[ix] == ':')) {
ix++;
}
next = data + ix;
return out;
};
//const char *ptr=s.c_str(); //const char *ptr=s.c_str();
//sscanf(s.c_str(), "%4hd%2hd%2hd%2hd%2hd%2hd", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); //sscanf(s.c_str(), "%4hd%2hd%2hd%2hd%2hd%2hd", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
st.wYear=atoi(s.substr(0, 4).c_str()); /*st.wYear = atoi(s.substr(0, 4).c_str());
st.wMonth=atoi(s.substr(4, 2).c_str()); st.wMonth=atoi(s.substr(4, 2).c_str());
st.wDay=atoi(s.substr(6, 2).c_str()); st.wDay=atoi(s.substr(6, 2).c_str());
st.wHour=atoi(s.substr(8, 2).c_str()); st.wHour=atoi(s.substr(8, 2).c_str());
st.wMinute=atoi(s.substr(10, 2).c_str()); st.wMinute=atoi(s.substr(10, 2).c_str());
st.wSecond=atoi(s.substr(12, 2).c_str()); st.wSecond=atoi(s.substr(12, 2).c_str());
*/
const char* ptr = s.data();
st.wYear = parse(ptr, 4, ptr);
st.wMonth = parse(ptr, 2, ptr);
st.wDay = parse(ptr, 2, ptr);
st.wHour = parse(ptr, 2, ptr);
st.wMinute = parse(ptr, 2, ptr);
st.wSecond = parse(ptr, 2, ptr);
FILETIME ft; FILETIME ft;
SystemTimeToFileTime(&st, &ft); SystemTimeToFileTime(&st, &ft);
__int64 &currenttime=*(__int64*)&ft; __int64 &currenttime=*(__int64*)&ft;
Time = unsigned((currenttime/10000000)-minYearConstant*365*24*3600); Time = unsigned((currenttime/10000000)-minYearConstant*365*24* timeConstSecPerHour);
} }

View File

@ -10,7 +10,7 @@
#endif // _MSC_VER > 1000 #endif // _MSC_VER > 1000
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -39,6 +39,8 @@ public:
const string &getStamp() const; const string &getStamp() const;
const string &getStamp(const string &sqlStampIn) const; const string &getStamp(const string &sqlStampIn) const;
const wstring getUpdateTime() const;
wstring getStampString() const; wstring getStampString() const;
string getStampStringN() const; string getStampStringN() const;
int getAge() const; int getAge() const;

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -42,7 +42,7 @@ AnimationData::AnimationData(gdioutput &gdi, int timePerPage, int nCol,
double w = (gdi.getWidth() + 20) *nCol + margin; double w = (gdi.getWidth() + 20) *nCol + margin;
double s = width / w; double s = width / w;
if ((fabs(s - 1.0) > 1e-3)) { if ((fabs(s - 1.0) > 1e-3)) {
gdi.scaleSize(s, true, false); gdi.scaleSize(s, true, gdioutput::ScaleOperation::NoRefresh);
} }
pageInfo.topMargin = 20; pageInfo.topMargin = 20;
pageInfo.scaleX = 1.0f; pageInfo.scaleX = 1.0f;

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -176,7 +176,10 @@ void AutoCompleteInfo::enter() {
} }
} }
void AutoCompleteInfo::setData(const vector<AutoCompleteRecord> &items) { void AutoCompleteInfo::setData(const vector<AutoCompleteRecord> &itemsIn) {
vector<AutoCompleteRecord> items = itemsIn;
stable_sort(items.begin(), items.end());
int newDataIx = -1; int newDataIx = -1;
if (modifedAutoComplete && size_t(currentIx) < data.size()) { if (modifedAutoComplete && size_t(currentIx) < data.size()) {
for (size_t k = 0; k < items.size(); k++) { for (size_t k = 0; k < items.size(); k++) {
@ -188,6 +191,6 @@ void AutoCompleteInfo::setData(const vector<AutoCompleteRecord> &items) {
newDataIx = 0; newDataIx = 0;
modifedAutoComplete = false; modifedAutoComplete = false;
} }
data = items; data = std::move(items);
currentIx = newDataIx; currentIx = newDataIx;
} }

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -29,10 +29,17 @@ class AutoCompleteHandler;
struct AutoCompleteRecord { struct AutoCompleteRecord {
AutoCompleteRecord() : id(-1) {} AutoCompleteRecord() : id(-1) {}
AutoCompleteRecord(const wstring &display, const wstring &name, int id) : display(display), name(name), id(id) {} AutoCompleteRecord(const wstring &display, int prio, const wstring &name, int id) : display(display), prio(prio), name(name), id(id) {}
wstring display; wstring display;
wstring name; wstring name;
int id; int id;
int prio = 0;
bool operator<(const AutoCompleteRecord& rec) const {
if (prio != rec.prio)
return prio > rec.prio;
else
return display < rec.display;
}
}; };
class AutoCompleteInfo { class AutoCompleteInfo {

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

204
code/binencoder.cpp Normal file
View File

@ -0,0 +1,204 @@
/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2023 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 "meos_util.h"
#include <vector>
#include <cassert>
#include "binencoder.h"
Encoder92::Encoder92() {
int d = 32;
fill(reverse_table, reverse_table + 128, 0);
fill(used, used + 128, false);
for (int j = 0; j < 92; j++) {
table[j] = d++;
while (d == '"' || d == '<' || d == '&' || d=='>')
d++;
}
table[91] = '\t';
swap(table[0], table[1]);
for (int j = 0; j < 92; j++) {
reverse_table[table[j]] = j;
used[table[j]] = true;
}
}
void Encoder92::encode92(const uint8_t datain[13], uint8_t dataout[16]) {
uint64_t w64bitA = *(uint64_t*)&datain[0];
uint32_t w32bitB = *(uint32_t*)&datain[8];
uint32_t w8bitC = datain[12];
// Consume 60 bits of data from w64bitA (4 bits will remain)
for (int i = 0; i < 10; i++) {
dataout[i] = w64bitA & 0b00111111;
w64bitA = w64bitA >> 6;
}
// Consume 30 bits of data from w32bitB (2 bits will remain)
for (int i = 10; i < 15; i++) {
dataout[i] = w32bitB & 0b00111111;
w32bitB = w32bitB >> 6;
}
// Consume remaining 2 + 4 bits
dataout[15] = int8_t(w32bitB | (w64bitA << 2));
for (int i = 0; i < 16; i+=2) {
int extraData = (w8bitC & 0x1);
w8bitC = w8bitC >> 1;
int combined = (extraData << 12) | dataout[i] | (dataout[i+1] << 6); // 13 bits 0 -- 8192
dataout[i] = combined % 92;
dataout[i+1] = combined / 92;
}
}
void Encoder92::decode92(const uint8_t datain[16], uint8_t dataout[13]) {
uint8_t datain_64[16];
uint32_t w8bitC = 0;
for (int i = 0; i < 16; i += 2) {
int combined = datain[i] + datain[i + 1] * 92;
datain_64[i] = combined & 0b00111111;
combined = combined >> 6;
datain_64[i+1] = combined & 0b00111111;
combined = combined >> 6;
w8bitC |= (combined << (i/2));
}
uint64_t w64bitA = 0;
uint32_t w32bitB = 0;
// Reconstruct 60 bits of data to w64bitA
for (int i = 9; i >= 0; i--) {
w64bitA <<= 6;
w64bitA |= datain_64[i];
}
// Reconstruct 30 bits of data to w32bitB
for (int i = 14; i >= 10; i--) {
w32bitB <<= 6;
w32bitB |= datain_64[i];
}
// Add remaining bits
w32bitB = w32bitB | ((datain_64[15] & 0b11) << 30);
uint64_t highBits = (datain_64[15] >> 2) & 0b1111;
w64bitA = w64bitA | (highBits << 60ll);
*((uint64_t*)&dataout[0]) = w64bitA;
*((uint32_t*)&dataout[8]) = w32bitB;
dataout[12] = int8_t(w8bitC);
}
void Encoder92::encode92(const vector<uint8_t>& bytesIn, string& encodedString) {
int m13 = bytesIn.size()%13;
int extra = m13 > 0 ? 13 - m13 : 0;
int blocks = (bytesIn.size() + extra) / 13;
encodedString = itos(bytesIn.size()) + ":";
int outLen = encodedString.length();
encodedString.resize(encodedString.size() + blocks * 16, ' ');
const uint8_t* inPtr = bytesIn.data();
//uint8_t* outPtr = encodedString.data() + outLen;
uint8_t datain[13];
fill(datain, datain + 13, 0);
uint8_t dataout[16];
int fullBlocks = blocks;
if (extra > 0)
fullBlocks--;
for (int i = 0; i < fullBlocks; i++) {
//for (int j = 0; j < 13; j++)
encode92(inPtr, dataout);
inPtr += 13;
for (int j = 0; j < 16; j++)
encodedString[outLen++] = table[dataout[j]];
}
if (extra > 0) {
for (int j = 0; j < m13; j++) {
datain[j] = inPtr[j];
}
encode92(datain, dataout);
for (int j = 0; j < 16; j++)
encodedString[outLen++] = table[dataout[j]];
}
}
void Encoder92::decode92(const string& encodedString, vector<uint8_t>& bytesOut) {
bytesOut.clear();
if (encodedString.empty())
return;
unsigned size = atoi(encodedString.c_str());
string start = itos(size);
int len = start.length();
if (encodedString.size() < len || encodedString[len] != ':' || size<0)
throw std::exception("Invalid data");
int m13 = size % 13;
int extra = m13 > 0 ? 13 - m13 : 0;
int blocks = (size + extra) / 13;
int inDataSize = blocks * 16;
if (encodedString.size() < len + 1 + inDataSize)
throw std::exception("Invalid data");
bytesOut.resize(size);
auto outPtr = bytesOut.data();
uint8_t datain[16];
uint8_t dataout[13];
int fullBlocks = blocks;
if (extra > 0)
fullBlocks--;
const char *inPtr = encodedString.c_str() + len + 1;
for (int i = 0; i < fullBlocks; i++) {
for (int j = 0; j < 16; j++) {
int v = inPtr[j];
if (v < 0 || v > 127 || !used[v])
throw std::exception("Invalid data");
datain[j] = reverse_table[inPtr[j]];
}
decode92(datain, outPtr);
inPtr += 16;
outPtr += 13;
}
if (extra > 0) {
for (int j = 0; j < 16; j++) {
int v = inPtr[j];
if (v < 0 || v > 127 || !used[v])
throw std::exception("Invalid data");
datain[j] = reverse_table[inPtr[j]];
}
decode92(datain, dataout);
for (int j = 0; j < m13; j++)
outPtr[j] = dataout[j];
}
}

39
code/binencoder.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2023 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 <cstdint>
class Encoder92 {
int8_t table[92];
int8_t reverse_table[128];
bool used[128];
public:
Encoder92();
void encode92(const uint8_t datain[13], uint8_t dataout[16]);
void decode92(const uint8_t datain[16], uint8_t dataout[13]);
void encode92(const vector<uint8_t>& bytesIn, string& encodedString);
void decode92(const string& encodedString, vector<uint8_t>& bytesOut);
};

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -220,7 +220,7 @@ void oEvent::getClassConfigurationInfo(ClassConfigInfo &cnf) const
for (size_t k = 0; k < it->getNumStages(); k++) { for (size_t k = 0; k < it->getNumStages(); k++) {
StartTypes st = it->getStartType(k); StartTypes st = it->getStartType(k);
if (st == STDrawn || st == STHunting) { if (st == STDrawn || st == STPursuit) {
cnf.legNStart[k].push_back(it->getId()); cnf.legNStart[k].push_back(it->getId());
if (cnf.timeStart.size() <= k) if (cnf.timeStart.size() <= k)
cnf.timeStart.resize(k+1); cnf.timeStart.resize(k+1);
@ -243,7 +243,7 @@ void oEvent::getClassConfigurationInfo(ClassConfigInfo &cnf) const
for (size_t k = 0; k < it->getNumStages(); k++) { for (size_t k = 0; k < it->getNumStages(); k++) {
StartTypes st = it->getStartType(k); StartTypes st = it->getStartType(k);
if (st == STDrawn || st == STHunting) { if (st == STDrawn || st == STPursuit) {
cnf.raceNStart[k].push_back(it->getId()); cnf.raceNStart[k].push_back(it->getId());
if (cnf.timeStart.size() <= k) if (cnf.timeStart.size() <= k)
cnf.timeStart.resize(k+1); cnf.timeStart.resize(k+1);

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -189,11 +189,10 @@ bool csvparser::importOS_CSV(oEvent &event, const wstring &file)
//Import runners! //Import runners!
int runner=0; int runner=0;
while( (rindex+OSRrentcard)<sp.size() && sp[rindex+OSRfname].length()>0 ){ while( (rindex+OSRrentcard)<sp.size() && sp[rindex+OSRfname].length()>0 ){
int year = extendYear(wtoi(sp[rindex+OSRyb]));
int cardNo = wtoi(sp[rindex+OSRcard]); int cardNo = wtoi(sp[rindex+OSRcard]);
wstring sname = sp[rindex+OSRsname] + L", " + sp[rindex+OSRfname]; wstring sname = sp[rindex+OSRsname] + L", " + sp[rindex+OSRfname];
pRunner r = event.addRunner(sname, ClubId, pRunner r = event.addRunner(sname, ClubId,
ClassId, cardNo, year, false); ClassId, cardNo, sp[rindex + OSRyb], false);
r->setEntrySource(externalSourceId); r->setEntrySource(externalSourceId);
oDataInterface DI=r->getDI(); oDataInterface DI=r->getDI();
@ -697,10 +696,10 @@ bool csvparser::importRAID(oEvent &event, const wstring &file)
} }
//Import runners! //Import runners!
pRunner r1=event.addRunner(sp[RAIDrunner1], ClubId, ClassId, 0, 0, false); pRunner r1=event.addRunner(sp[RAIDrunner1], ClubId, ClassId, 0, L"", false);
team->setRunner(0, r1, false); team->setRunner(0, r1, false);
pRunner r2=event.addRunner(sp[RAIDrunner2], ClubId, ClassId, 0, 0, false); pRunner r2=event.addRunner(sp[RAIDrunner2], ClubId, ClassId, 0, L"", false);
team->setRunner(1, r2, false); team->setRunner(1, r2, false);
team->evaluate(oBase::ChangeType::Update); team->evaluate(oBase::ChangeType::Update);
@ -812,15 +811,16 @@ bool csvparser::importPunches(const oEvent &oe, const wstring &file, vector<Punc
if (ret == -1) if (ret == -1)
return false; // Invalid file return false; // Invalid file
if (ret > 0) { if (ret > 0) {
int card = wtoi(sp[cardIndex]); const int card = wtoi(sp[cardIndex]);
int time = oe.getRelativeTime(processedTime); const int time = oe.getRelativeTime(processedTime);
if (card>0) { if (card>0) {
PunchInfo pi; PunchInfo pi;
pi.card = card; pi.card = card;
pi.time = time; pi.time = time;
string pd(processedDate.begin(), processedDate.end()); //string pd(processedDate.begin(), processedDate.end());
strncpy_s(pi.date, sizeof(pi.date), pd.c_str(), 26); string pd = gdioutput::narrow(processedDate);
strncpy_s(pi.date, pd.c_str(), 26);
pi.date[26] = 0; pi.date[26] = 0;
punches.push_back(pi); punches.push_back(pi);
nimport++; nimport++;
@ -1204,7 +1204,7 @@ void csvparser::parse(const wstring &file, list< vector<wstring> > &data) {
string rbf; string rbf;
if (!fin.good()) if (!fin.good())
throw meosException("Failed to read file"); throw meosException(L"Failed to read file, " + file);
bool isUTF8 = false; bool isUTF8 = false;
bool firstLine = true; bool firstLine = true;

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -7,7 +7,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -162,10 +162,11 @@ ClassResultFraction = Fraction of class complete
ClassStartName = Start name ClassStartName = Start name
ClassStartTime = Class, start time, name ClassStartTime = Class, start time, name
ClassStartTimeClub = Class, start time, club ClassStartTimeClub = Class, start time, club
ClubClassStartTime = Club, class, start time
ClassTotalResult = Class, total result ClassTotalResult = Class, total result
Club = Club Club = Club
ClubName = Club ClubName = Club
ClubRunner = Club (competitor) ClubRunner = Club (Competitors)
CmpDate = Competition date CmpDate = Competition date
CmpName = Competition name CmpName = Competition name
CourseClimb = Course climb CourseClimb = Course climb
@ -282,7 +283,7 @@ FilterHasCard = With card
FilterNoCard = Without card FilterNoCard = Without card
FilterNotVacant = Not vacant FilterNotVacant = Not vacant
FilterOnlyVacant = Only vacant FilterOnlyVacant = Only vacant
FilterRentCard = Borrowed card FilterRentCard = Hired card
FilterResult = With result FilterResult = With result
FilterStarted = Has started FilterStarted = Has started
Filtrering = Filtering Filtrering = Filtering
@ -357,10 +358,10 @@ Hjälp = Help
Hoppar över stafettklass: X = Skipping relay class: X Hoppar över stafettklass: X = Skipping relay class: X
Huvudlista = Main list Huvudlista = Main list
Hyravgift = Card hire Hyravgift = Card hire
Hyrbricka = Borrowed card Hyrbricka = Hired card
Hyrbricksrapport = Report with Borrowed Cards Hyrbricksrapport = Report with Hired Cards
Hyrbricksrapport - %s = Borrowed Cards - %s Hyrbricksrapport - %s = Hired Cards - %s
Hyrd = Borrowed Hyrd = Hired
Hämta (efter)anmälningar från Eventor = Fetch (late) entries from Eventor Hämta (efter)anmälningar från Eventor = Fetch (late) entries from Eventor
Hämta data från Eventor = Fetch Data from Eventor Hämta data från Eventor = Fetch Data from Eventor
Hämta efteranmälningar = Fetch Late Entries Hämta efteranmälningar = Fetch Late Entries
@ -393,7 +394,7 @@ Importera = Import
Importera IOF (xml) = Import IOF (xml) Importera IOF (xml) = Import IOF (xml)
Importera anmälningar = Import Entries Importera anmälningar = Import Entries
Importera banor = Import Courses Importera banor = Import Courses
Importera banor/klasser = Import courses/classes Importera banor/klasser = Import Courses and Classes
Importera en tävling från fil = Import competition from file Importera en tävling från fil = Import competition from file
Importera fil = Import File Importera fil = Import File
Importera från OCAD = Import from OCAD Importera från OCAD = Import from OCAD
@ -1041,7 +1042,7 @@ TeamClub = Team's club
TeamLegTimeAfter = Team's time behind on leg TeamLegTimeAfter = Team's time behind on leg
TeamLegTimeStatus = Team's time / status on leg TeamLegTimeStatus = Team's time / status on leg
TeamName = Team name TeamName = Team name
TeamPlace = Team's place TeamPlace = Team's place after leg
TeamRogainingPoint = Team's rogaining points TeamRogainingPoint = Team's rogaining points
TeamRunner = Name of team member TeamRunner = Name of team member
TeamRunnerCard = Card number of team member TeamRunnerCard = Card number of team member
@ -1242,7 +1243,7 @@ Välj vilka kolumner du vill visa = Choose columns to show
Välj vy = Choose view Välj vy = Choose view
Välkommen till MeOS = Welcome to MeOS Välkommen till MeOS = Welcome to MeOS
Vänligen betala senast = Please pay at latest Vänligen betala senast = Please pay at latest
Vänligen återlämna hyrbrickan = Please return your borrowed card Vänligen återlämna hyrbrickan = Please return your hired card
Växling = Changeover Växling = Changeover
Webb = Web Document Webb = Web Document
Webbdokument = Web document Webbdokument = Web document
@ -1319,7 +1320,7 @@ help:41072 = Select a punch from the list to change or remove it. You can add mi
help:41641 = Enter a first start time and start interval. Draw random gives an unconditionally random start order. Swedish draw method uses special rules to distribute runners from the same club. Clumped start means the whole class starts in small groups during the specified interval (extended mass start). In the field leg, you can specify which leg is to be drawn, if the class has several. help:41641 = Enter a first start time and start interval. Draw random gives an unconditionally random start order. Swedish draw method uses special rules to distribute runners from the same club. Clumped start means the whole class starts in small groups during the specified interval (extended mass start). In the field leg, you can specify which leg is to be drawn, if the class has several.
help:425188 = You can handle runners that didn't start automatically by reading out SI stations (clear/check/start/controls) in SportIdent Config+. Save the readout as a semi colon separated text file and import this file into MeOS. Runners in this import get a registration. Then you can give DNS status to all runners without registration. If you later import more runners, you can reset the status (from DNS to Unknown) on the runners now imported. help:425188 = You can handle runners that didn't start automatically by reading out SI stations (clear/check/start/controls) in SportIdent Config+. Save the readout as a semi colon separated text file and import this file into MeOS. Runners in this import get a registration. Then you can give DNS status to all runners without registration. If you later import more runners, you can reset the status (from DNS to Unknown) on the runners now imported.
info:readoutbase = Activate the SI unit by selecting its COM-port, or by searching for installed SI units. Press Information to get status for the selected port.\n\nInteractive readout lets you directly handle problems, such as wrong card number. Do not use this option when runners with problems are handled separately.\n\nThe runner database is used if you want to automatically add new runners. The punches are used to find (guess) the right class. info:readoutbase = Activate the SI unit by selecting its COM-port, or by searching for installed SI units. Press Information to get status for the selected port.\n\nInteractive readout lets you directly handle problems, such as wrong card number. Do not use this option when runners with problems are handled separately.\n\nThe runner database is used if you want to automatically add new runners. The punches are used to find (guess) the right class.
info:readoutmore = Sound selection lets you enable sound signals to be played back on reading out a card.\n\nShow readout in window opens a new window designed to be shown on a screen turned towards the competitor.\n\nIf you have connected a punching unit (for example finish punch) you can decide if you want to accept start, radio, or finish punches. This is a filter to prevent (for example) unexpected start punches from being recieved by mistake, which could overwrite the drawn starting time. info:readoutmore = Lock the function to prevent accidental changes.\n\nSound selection lets you enable sound signals to be played back on reading out a card.\n\nOpen Readout Window shows a new window designed to be shown on a screen turned towards the competitor, showing information about the last readout.\n\nSeveral races per competitor can be used if you are allowed do several attempts on the course. A new entry is created for each readout.
help:50431 = You are now connected to a server. To open a competition on the server, select it in the list and click open. Do add a competition to the server, open the competition locally and select upload. When you have opened a competition on the server, you will see all other connected MeOS clients. help:50431 = You are now connected to a server. To open a competition on the server, select it in the list and click open. Do add a competition to the server, open the competition locally and select upload. When you have opened a competition on the server, you will see all other connected MeOS clients.
help:52726 = Connect to a server below.\n\nInstallation\nDownload and install MySQL 5 (Community Edition) from www.mysql.com. You can use default settings. It is only necessary to install MySQL on the computer acting server. When MySQL is installed, start MySQL Command Line Client and create a user account for MeOS. You write like this:\n\n> CREATE USER meos;\nGRANT ALL ON *.* TO meos;\n\nYou have now created a user meos with no password. Enter the name of the server below (you may have to configure firewalls to let through the traffic).\n\nAs an alternative you can use the built-in root account of MySQL. User name is 'root' and password is the one you provided when installing MySQL. help:52726 = Connect to a server below.\n\nInstallation\nDownload and install MySQL 5 (Community Edition) from www.mysql.com. You can use default settings. It is only necessary to install MySQL on the computer acting server. When MySQL is installed, start MySQL Command Line Client and create a user account for MeOS. You write like this:\n\n> CREATE USER meos;\nGRANT ALL ON *.* TO meos;\n\nYou have now created a user meos with no password. Enter the name of the server below (you may have to configure firewalls to let through the traffic).\n\nAs an alternative you can use the built-in root account of MySQL. User name is 'root' and password is the one you provided when installing MySQL.
help:5422 = Found no SI unit. Are they connected and started? help:5422 = Found no SI unit. Are they connected and started?
@ -2596,3 +2597,64 @@ Duplicerad nummerlapp: X, Y = Duplicated bib: X, Y
Saknat lag mellan X och Y = Missing team between X and Y Saknat lag mellan X och Y = Missing team between X and Y
Lag utan nummerlapp: X = Team with no bib: X Lag utan nummerlapp: X = Team with no bib: X
Brickavläsning = Card Readout Brickavläsning = Card Readout
Ett långt tävlingsnamn kan ge oväntad nerskalning av utskrifter = A long competition name may cause unwanted downscale of printed lists
Ingen reducerad avgift = No reduced fee
Reducerad avgift = Reduced fee
Reducerad avgift för = Reduced fee for
Unga, till och med X år = Young people, up to X years
Ungdomar och äldre kan få reducerad avgift = Elder and younger people can have a reduced fee
Äldre, från och med X år = Elders, X years and above
Programmera stationen utan AUTOSEND = Program the unit with AUTO SEND off
Aktivera stöd för tiondels sekunder = Activate support for subsecond timing.
Automatisk hyrbrickshantering genom registrerade hyrbrickor = Automatic handling of hired cards using pre-registered card numbers
prefsAutoTieRent = Automatic handling of hired cards
prefsExpResFilename = Default export filename
prefsExpTypeIOF = Default export type
prefsExpWithRaceNo = Include race number when exporting
Ignorerade X duplikat = Ignored X duplicates
info:customsplitprint = You can use a cusom list for the split printout. Design the list and use the function 'For Split Times' in the list editor to make changes for split printout.\n\nYou can control the list to use per class by using the table mode.
Sträcktidslista = Split time list
info:nosplitprint = Cannot load the specified list.\n\nUsing defaults instead.
Välj deltagare för förhandsgranskning = Select competitor for preview
Inkludera bomanalys = Include analysis of lost time
Inkludera individuellt resultat = Include indiviual result
Inkludera sträcktider = Include splits
Inkludera tempo = Include tempo
Skapa en klass för varje bana = Create a class for each course
Ingen[competition] = None
Basera på en tidigare tävling = Base on existing competition
Baserad på X = Based on X
info:multiple_start = A competitor may do multiple starts with the same card. Automatic new entry for each readout.
Flera starter per deltagare = Several races per competitor
Image = Image
NumEntries = Number of entries
NumStarts = Numer of starts
TotalRunLength = Total running distance
TotalRunTime = Total running time
X stämplingar = X punches
RunnerBirthDate = Birth date
Avbryt inläsning = Cancel readout
Spara oparad bricka = Save unpaired card
warn:changeid = The field External Id is usually used to match entities with other databases (such as entry, result, or economy systems). If you make incomaptible changes, various and hard-to-understand problems might arise.
Cannot represent ID X = Cannot represent ID 'X'
Batteridatum = Battery date
Enhet = Unit
Öppna avläsningsfönster = Open readout window
info:readoutwindow = The readout window shows information on the latest card readout.
info:mapcontrol = MeOS is unable to determine what function a unit has unless it is directly attached to the computer. Therefore the programmed punch code is used to determine the type. You can control the interpretation below. Numbers higher than 30 are always interpreted as controls.\n\nBe careful when using start punches; they may permanently overwrite the drawn starting time.
Checkenhet = Check unit
Målenhet = Finish unit
Startenhet = Start unit
Du kan justera tiden för en viss enhet = You can adjust the time for a single unit
ClubTeam = Club (Teams)
CourseNumber = Course number
Enhetskod = Unit code
Tolkning av radiostämplingar med okänd typ = Interpretation of punches of unknown type
TeamCourseName = Course name for team/leg
TeamCourseNumber = Course number for team/leg
TeamLegName = Namn på sträcka
Du måste ange minst två gafflingsvarienater = You must specify at least 2 variants
Max antal gaffllingsvarianter att skapa = Maximum number of forking keys to create
Det uppskattade antalet startade lag i klassen är ett lämpligt värde = The estimated number of teams in the class is a suitable value
Lägg till bild = Add image
Använd listan för sträcktidsutskrift = Use list for split time printing

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -61,6 +61,7 @@ constexpr int absolutePosition = 1 << 17;
constexpr int skipBoundingBox = 1 << 18; constexpr int skipBoundingBox = 1 << 18;
constexpr int hiddenText = 1 << 19; constexpr int hiddenText = 1 << 19;
constexpr int textLimitEllipsis = 1 << 20; constexpr int textLimitEllipsis = 1 << 20;
constexpr int imageNoUpdatePos = 1 << 21;
enum GDICOLOR { enum GDICOLOR {
colorBlack = RGB(0, 0, 0), colorBlack = RGB(0, 0, 0),

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -99,7 +99,7 @@ class AutoCompleteInfo;
class Recorder; class Recorder;
class gdioutput { class gdioutput {
protected: protected:
string tag; string tag;
// Database error state warning // Database error state warning
@ -109,30 +109,30 @@ protected:
bool useTables; bool useTables;
// Set to true when in test mode // Set to true when in test mode
bool isTestMode; bool isTestMode;
bool highContrast; bool highContrast;
void deleteFonts(); void deleteFonts();
void constructor(double _scale); void constructor(double _scale);
void updateStringPosCache(); void updateStringPosCache();
vector<TextInfo *> shownStrings; vector<TextInfo*> shownStrings;
void enableCheckBoxLink(TextInfo &ti, bool enable); void enableCheckBoxLink(TextInfo& ti, bool enable);
//void CalculateCS(TextInfo &text); //void CalculateCS(TextInfo &text);
//void printPage(PrinterObject &po, int StartY, int &EndY, bool calculate); //void printPage(PrinterObject &po, int StartY, int &EndY, bool calculate);
void printPage(PrinterObject &po, const PageInfo &pageInfo, RenderedPage &page); void printPage(PrinterObject& po, const PageInfo& pageInfo, RenderedPage& page);
bool startDoc(PrinterObject &po); bool startDoc(PrinterObject& po);
bool getSelectedItem(ListBoxInfo &lbi); bool getSelectedItem(ListBoxInfo& lbi);
bool doPrint(PrinterObject &po, PageInfo &pageInfo, pEvent oe, bool respectPageBreak); bool doPrint(PrinterObject& po, PageInfo& pageInfo, pEvent oe, bool respectPageBreak);
PrinterObject *po_default; PrinterObject* po_default;
void restoreInternal(const RestoreInfo &ri); void restoreInternal(const RestoreInfo& ri);
void drawCloseBox(HDC hDC, RECT &Close, bool pressed); void drawCloseBox(HDC hDC, RECT& Close, bool pressed);
void setFontCtrl(HWND hWnd); void setFontCtrl(HWND hWnd);
@ -145,14 +145,20 @@ protected:
//by avoiding to loop through complete TL. //by avoiding to loop through complete TL.
list<TextInfo>::iterator itTL; list<TextInfo>::iterator itTL;
// References into TL for all images
vector<TextInfo*> imageReferences;
// Needed after removing images to clear unused references
void updateImageReferences();
list<ButtonInfo> BI; list<ButtonInfo> BI;
unordered_map<HWND, ButtonInfo *> biByHwnd; unordered_map<HWND, ButtonInfo*> biByHwnd;
list<InputInfo> II; list<InputInfo> II;
unordered_map<HWND, InputInfo *> iiByHwnd; unordered_map<HWND, InputInfo*> iiByHwnd;
list<ListBoxInfo> LBI; list<ListBoxInfo> LBI;
unordered_map<HWND, ListBoxInfo *> lbiByHwnd; unordered_map<HWND, ListBoxInfo*> lbiByHwnd;
list<DataStore> DataInfo; list<DataStore> DataInfo;
list<EventInfo> Events; list<EventInfo> Events;
@ -160,7 +166,7 @@ protected:
list<TableInfo> Tables; list<TableInfo> Tables;
list<TimerInfo> timers; list<TimerInfo> timers;
Toolbar *toolbar; Toolbar* toolbar;
ToolList toolTips; ToolList toolTips;
map<string, RestoreInfo> restorePoints; map<string, RestoreInfo> restorePoints;
@ -191,10 +197,10 @@ protected:
mutable map<pair<int, wstring>, int> fontHeightCache; mutable map<pair<int, wstring>, int> fontHeightCache;
map<wstring, GDIImplFontSet> fonts; map<wstring, GDIImplFontSet> fonts;
const GDIImplFontSet &getCurrentFont() const; const GDIImplFontSet& getCurrentFont() const;
const GDIImplFontSet &getFont(const wstring &font) const; const GDIImplFontSet& getFont(const wstring& font) const;
const GDIImplFontSet &loadFont(const wstring &font); const GDIImplFontSet& loadFont(const wstring& font);
mutable const GDIImplFontSet *currentFontSet; mutable const GDIImplFontSet* currentFontSet;
int MaxX; int MaxX;
int MaxY; int MaxY;
@ -203,16 +209,25 @@ protected:
int SX; int SX;
int SY; int SY;
int Direction; enum class FlowDirection {
Down,
Right,
None,
};
FlowDirection flowDirection;
int OffsetY; //Range 0 -- MaxY int OffsetY; //Range 0 -- MaxY
int OffsetX; //Range 0 -- MaxX int OffsetX; //Range 0 -- MaxX
// Maximum height of a text block of the current view.
int maxTextBlockHeight = 0;
//Set to true if we should not update window during "addText" operations //Set to true if we should not update window during "addText" operations
bool manualUpdate; bool manualUpdate;
LRESULT ProcessMsgWrp(UINT iMessage, LPARAM lParam, WPARAM wParam); LRESULT ProcessMsgWrp(UINT iMessage, LPARAM lParam, WPARAM wParam);
void getWindowText(HWND hWnd, wstring &text); void getWindowText(HWND hWnd, wstring& text);
double scale; double scale;
HFONT getGUIFont() const; HFONT getGUIFont() const;
@ -223,18 +238,18 @@ protected:
mutable DWORD lastColor; mutable DWORD lastColor;
mutable wstring lastFont; mutable wstring lastFont;
void initCommon(double scale, const wstring &font); void initCommon(double scale, const wstring& font);
void processButtonMessage(ButtonInfo &bi, WPARAM wParam); void processButtonMessage(ButtonInfo& bi, WPARAM wParam);
void processEditMessage(InputInfo &bi, WPARAM wParam); void processEditMessage(InputInfo& bi, WPARAM wParam);
void processComboMessage(ListBoxInfo &bi, WPARAM wParam); void processComboMessage(ListBoxInfo& bi, WPARAM wParam);
void processListMessage(ListBoxInfo &bi, WPARAM wParam); void processListMessage(ListBoxInfo& bi, WPARAM wParam);
void doEnter(); void doEnter();
void doEscape(); void doEscape();
bool doUpDown(int direction); bool doUpDown(int direction);
FixedTabs *tabs; FixedTabs* tabs;
wstring currentFont; wstring currentFont;
vector< GDIImplFontEnum > enumeratedFonts; vector< GDIImplFontEnum > enumeratedFonts;
@ -265,7 +280,7 @@ protected:
wstring str; wstring str;
bool reached; bool reached;
ScreenStringInfo(const RECT &r, const wstring &s) { ScreenStringInfo(const RECT& r, const wstring& s) {
rc = r; rc = r;
str = s; str = s;
reached = false; reached = false;
@ -283,146 +298,156 @@ protected:
friend class TestMeOS; friend class TestMeOS;
// Recorder, the second member is true if the recorder is owned and should be deleted // Recorder, the second member is true if the recorder is owned and should be deleted
pair<Recorder *, bool> recorder; pair<Recorder*, bool> recorder;
list< pair<const SubCommand *, string> > subCommands; list< pair<const SubCommand*, string> > subCommands;
shared_ptr<AnimationData> animationData; shared_ptr<AnimationData> animationData;
shared_ptr<AutoCompleteInfo> autoCompleteInfo; shared_ptr<AutoCompleteInfo> autoCompleteInfo;
wstring delayedAlert; wstring delayedAlert;
struct GuiMeasure {
int height = 0;
int extraX = 0;
int letterWidth = 0;
float avgCharWidth = 0;
};
mutable shared_ptr<GuiMeasure> guiMeasure;
public: public:
AutoCompleteInfo &addAutoComplete(const string &key); AutoCompleteInfo& addAutoComplete(const string& key);
void clearAutoComplete(const string &key); void clearAutoComplete(const string& key);
bool hasAutoComplete() const { return autoCompleteInfo != nullptr; } bool hasAutoComplete() const { return autoCompleteInfo != nullptr; }
// Return the bounding dimension of the desktop // Return the bounding dimension of the desktop
void getVirtualScreenSize(RECT &rc); void getVirtualScreenSize(RECT& rc);
void getWindowsPosition(RECT &rc) const; void getWindowsPosition(RECT& rc) const;
void setWindowsPosition(const RECT &rc); void setWindowsPosition(const RECT& rc);
void initRecorder(Recorder *rec);
Recorder &getRecorder();
string dbPress(const string &id, int extra);
string dbPress(const string &id, const char *extra);
string dbSelect(const string &id, int data);
void dbInput(const string &id, const string &test);
void dbCheck(const string &id, bool state);
string dbClick(const string &id, int extra);
void dbDblClick(const string &id, int data);
void dbRegisterSubCommand(const SubCommand *cmd, const string &action); void initRecorder(Recorder* rec);
Recorder& getRecorder();
string dbPress(const string& id, int extra);
string dbPress(const string& id, const char* extra);
string dbSelect(const string& id, int data);
void dbInput(const string& id, const string& test);
void dbCheck(const string& id, bool state);
string dbClick(const string& id, int extra);
void dbDblClick(const string& id, int data);
void dbRegisterSubCommand(const SubCommand* cmd, const string& action);
void runSubCommand(); void runSubCommand();
// Add the next answer for a dialog popup // Add the next answer for a dialog popup
void dbPushDialogAnswer(const string &answer); void dbPushDialogAnswer(const string& answer);
mutable list<string> cmdAnswers; mutable list<string> cmdAnswers;
int dbGetStringCount(const string &str, bool subString) const; int dbGetStringCount(const string& str, bool subString) const;
// Ensure list of stored answers is empty // Ensure list of stored answers is empty
void clearDialogAnswers(bool checkEmpty); void clearDialogAnswers(bool checkEmpty);
void internalSelect(ListBoxInfo &bi); void internalSelect(ListBoxInfo& bi);
bool isTest() const {return isTestMode;} bool isTest() const { return isTestMode; }
const string &getTag() const {return tag;} const string& getTag() const { return tag; }
bool hasTag(const string &t) const {return tag == t;} bool hasTag(const string& t) const { return tag == t; }
static const wstring &recodeToWide(const string &input); static const wstring& recodeToWide(const string& input);
static const string &recodeToNarrow(const wstring &input); static const string& recodeToNarrow(const wstring& input);
static const wstring &widen(const string &input); static const wstring& widen(const string& input);
static const string &narrow(const wstring &input); static const string& narrow(const wstring& input);
static const string &toUTF8(const wstring &input); static const string& toUTF8(const wstring& input);
static const wstring &fromUTF8(const string &input); static const wstring& fromUTF8(const string& input);
//void setEncoding(FontEncoding encoding); //void setEncoding(FontEncoding encoding);
//FontEncoding getEncoding() const; //FontEncoding getEncoding() const;
void getFontInfo(const TextInfo &ti, FontInfo &fi) const; void getFontInfo(const TextInfo& ti, FontInfo& fi) const;
/** Return true if rendering text should be skipped for /** Return true if rendering text should be skipped for
this format. */ this format. */
static bool skipTextRender(int format); static bool skipTextRender(int format);
const list<TextInfo> &getTL() const {return TL;} const list<TextInfo>& getTL() const { return TL; }
void getEnumeratedFonts(vector< pair<wstring, size_t> > &output) const; void getEnumeratedFonts(vector< pair<wstring, size_t> >& output) const;
const wstring &getFontName(int id); const wstring& getFontName(int id);
double getRelativeFontScale(gdiFonts font, const wchar_t *fontFace) const; double getRelativeFontScale(gdiFonts font, const wchar_t* fontFace) const;
bool isFullScreen() const {return fullScreen;} bool isFullScreen() const { return fullScreen; }
void setFullScreen(bool useFullScreen); void setFullScreen(bool useFullScreen);
void setColorMode(DWORD bgColor1, DWORD bgColor2 = -1, void setColorMode(DWORD bgColor1, DWORD bgColor2 = -1,
DWORD fgColor = -1, const wstring &bgImage = L""); DWORD fgColor = -1, const wstring& bgImage = L"");
DWORD getFGColor() const; DWORD getFGColor() const;
DWORD getBGColor() const; DWORD getBGColor() const;
DWORD getBGColor2() const; DWORD getBGColor2() const;
const wstring &getBGImage() const; const wstring& getBGImage() const;
void setAnimationMode(const shared_ptr<AnimationData> &mode); void setAnimationMode(const shared_ptr<AnimationData>& mode);
void setAutoScroll(double speed); void setAutoScroll(double speed);
void getAutoScroll(double &speed, double &pos) const; void getAutoScroll(double& speed, double& pos) const;
void storeAutoPos(double pos); void storeAutoPos(double pos);
int getAutoScrollDir() const {return (autoSpeed > 0 ? 1:-1);} int getAutoScrollDir() const { return (autoSpeed > 0 ? 1 : -1); }
int setHighContrastMaxWidth(); int setHighContrastMaxWidth();
void hideBackground(bool hide) {hideBG = hide;} void hideBackground(bool hide) { hideBG = hide; }
HWND getToolbarWindow() const; HWND getToolbarWindow() const;
bool hasToolbar() const; bool hasToolbar() const;
void activateToolbar(bool active); void activateToolbar(bool active);
void processToolbarMessage(const string &id, Table *data); void processToolbarMessage(const string& id, Table* data);
void synchronizeListScroll(const string &id1, const string &id2); void synchronizeListScroll(const string& id1, const string& id2);
FixedTabs &getTabs(); FixedTabs& getTabs();
// True if up/down is locked, i.e, don't move page // True if up/down is locked, i.e, don't move page
bool lockUpDown; bool lockUpDown;
double getScale() const {return scale;} double getScale() const { return scale; }
void enableEditControls(bool enable, bool processAll = false); void enableEditControls(bool enable, bool processAll = false);
bool hasEditControl() const; bool hasEditControl() const;
void setFont(int size, const wstring &font); void setFont(int size, const wstring& font);
int getButtonHeight() const; int getButtonHeight() const;
int scaleLength(int input) const {return int(scale*input + 0.5);} int scaleLength(int input) const { return int(scale * input + 0.5); }
// Fill in current printer settings // Fill in current printer settings
void fetchPrinterSettings(PrinterObject &po) const; void fetchPrinterSettings(PrinterObject& po) const;
void tableCB(ButtonInfo &bu, Table *t); void tableCB(ButtonInfo& bu, Table* t);
wchar_t *getExtra(const char *id) const; wchar_t* getExtra(const char* id) const;
int getExtraInt(const char *id) const; int getExtraInt(const char* id) const;
void enableTables(); void enableTables();
void disableTables(); void disableTables();
void pasteText(const char *id); void pasteText(const char* id);
void print(pEvent oe, Table *t = 0, bool printMeOSHeader = true, bool noMargin = false, bool respectPageBreak = true); void print(pEvent oe, Table* t = nullptr, bool printMeOSHeader = true, bool noMargin = false, bool respectPageBreak = true);
void print(PrinterObject &po, pEvent oe, bool printMeOSHeader = true, bool noMargin=false, bool respectPageBreak = true); void print(PrinterObject& po, pEvent oe, bool printMeOSHeader = true, bool noMargin = false, bool respectPageBreak = true);
void printSetup(PrinterObject &po); void printSetup(PrinterObject& po);
void destroyPrinterDC(PrinterObject &po); void destroyPrinterDC(PrinterObject& po);
void setSelection(const string &id, const set<int> &selection); void setSelection(const string& id, const set<int>& selection);
void setSelection(const wstring &id, const set<int> &selection) { void setSelection(const wstring& id, const set<int>& selection) {
setSelection(narrow(id), selection); setSelection(narrow(id), selection);
} }
void getSelection(const string &id, set<int> &selection);
HWND getHWNDTarget() const {return hWndTarget;} void getSelection(const string& id, set<int>& selection);
HWND getHWNDMain() const {return hWndAppMain;}
HWND getHWNDTarget() const { return hWndTarget; }
HWND getHWNDMain() const { return hWndAppMain; }
void scrollToBottom(); void scrollToBottom();
void scrollTo(int x, int y); void scrollTo(int x, int y);
@ -430,84 +455,88 @@ public:
void selectTab(int Id); void selectTab(int Id);
void addTable(const shared_ptr<Table> &table, int x, int y); void addTable(const shared_ptr<Table>& table, int x, int y);
Table &getTable() const; //Get the (last) table. If needed, add support for named tables... Table& getTable() const; //Get the (last) table. If needed, add support for named tables...
ToolInfo &addToolTip(const string &id, const wstring &tip, HWND hWnd, RECT *rc=0); ToolInfo& addToolTip(const string& id, const wstring& tip, HWND hWnd, RECT* rc = nullptr);
ToolInfo *getToolTip(const string &id); ToolInfo* getToolTip(const string& id);
ToolInfo &updateToolTip(const string &id, const wstring &tip); ToolInfo& updateToolTip(const string& id, const wstring& tip);
HWND getToolTip(){return hWndToolTip;} HWND getToolTip() { return hWndToolTip; }
void init(HWND hWnd, HWND hMainApp, HWND hTab); void init(HWND hWnd, HWND hMainApp, HWND hTab);
bool openDoc(const wchar_t *doc); bool openDoc(const wchar_t* doc);
wstring browseForSave(const vector< pair<wstring, wstring> > &filter, wstring browseForSave(const vector< pair<wstring, wstring> >& filter,
const wstring &defext, int &FilterIndex); const wstring& defext, int& FilterIndex);
wstring browseForOpen(const vector< pair<wstring, wstring> > &filter, wstring browseForOpen(const vector< pair<wstring, wstring> >& filter,
const wstring &defext); const wstring& defext);
wstring browseForFolder(const wstring &folderStart, const wchar_t *descr); wstring browseForFolder(const wstring& folderStart, const wchar_t* descr);
bool clipOffset(int PageX, int PageY, int &MaxOffsetX, int &MaxOffsetY);
RectangleInfo &addRectangle(RECT &rc, GDICOLOR Color = GDICOLOR(-1),
bool DrawBorder = true, bool addFirst = false);
RectangleInfo &getRectangle(const char *id);
DWORD makeEvent(const string &id, const string &origin,
DWORD data, int extraData, bool flushEvent);
void unregisterEvent(const string &id); bool clipOffset(int PageX, int PageY, int& MaxOffsetX, int& MaxOffsetY);
EventInfo &registerEvent(const string &id, GUICALLBACK cb); RectangleInfo& addRectangle(const RECT& rc, GDICOLOR Color = GDICOLOR(-1),
bool DrawBorder = true, bool addFirst = false);
int sendCtrlMessage(const string &id); RectangleInfo& getRectangle(const char* id);
DWORD makeEvent(const string& id, const string& origin,
DWORD data, int extraData, bool flushEvent);
void unregisterEvent(const string& id);
EventInfo& registerEvent(const string& id, GUICALLBACK cb);
int sendCtrlMessage(const string& id);
bool canClear(); bool canClear();
void setOnClearCb(GUICALLBACK cb); void setOnClearCb(GUICALLBACK cb);
void setPostClearCb(GUICALLBACK cb); void setPostClearCb(GUICALLBACK cb);
void restore(const string &id="", bool DoRefresh=true); void restore(const string& id = "", bool DoRefresh = true);
/// Restore, but do not update client area size, /// Restore, but do not update client area size,
/// position, zoom, scrollbars, and do not refresh /// position, zoom, scrollbars, and do not refresh
void restoreNoUpdate(const string &id); void restoreNoUpdate(const string& id);
void setRestorePoint(); void setRestorePoint();
void setRestorePoint(const string &id); void setRestorePoint(const string& id);
bool removeWidget(const string &id); bool removeWidget(const string& id);
bool hideWidget(const string &id, bool hide = true); bool hideWidget(const string& id, bool hide = true);
// Get and set restore point presence for a widget
bool getWidgetRestorePoint(const string& id, string& restorePoint) const;
void setWidgetRestorePoint(const string& id, const string& restorePoint);
void CheckInterfaceTimeouts(DWORD T); void CheckInterfaceTimeouts(DWORD T);
bool RemoveFirstInfoBox(const string &id); bool RemoveFirstInfoBox(const string& id);
void drawBoxText(HDC hDC, RECT &tr, InfoBox &Box, bool highligh); void drawBoxText(HDC hDC, RECT& tr, InfoBox& Box, bool highligh);
void drawBoxes(HDC hDC, RECT &rc); void drawBoxes(HDC hDC, RECT& rc);
void drawBox(HDC hDC, InfoBox &Box, RECT &pos); void drawBox(HDC hDC, InfoBox& Box, RECT& pos);
void addInfoBox(string id, wstring text, int TimeOut=0, GUICALLBACK cb=0); void addInfoBox(string id, wstring text, int TimeOut = 0, GUICALLBACK cb = nullptr);
void updateObjectPositions(); void updateObjectPositions();
void drawBackground(HDC hDC, RECT &rc); void drawBackground(HDC hDC, RECT& rc);
void renderRectangle(HDC hDC, RECT *clipRegion, const RectangleInfo &ri); void renderRectangle(HDC hDC, RECT* clipRegion, const RectangleInfo& ri);
void updateScrollbars() const; void updateScrollbars() const;
void setOffsetY(int oy) {OffsetY=oy;} void setOffsetY(int oy) { OffsetY = oy; }
void setOffsetX(int ox) {OffsetX=ox;} void setOffsetX(int ox) { OffsetX = ox; }
int getPageY() const; int getPageY() const;
int getPageX() const; int getPageX() const;
int getOffsetY() const {return OffsetY;} int getOffsetY() const { return OffsetY; }
int getOffsetX() const {return OffsetX;} int getOffsetX() const { return OffsetX; }
void RenderString(TextInfo &ti, const wstring &text, HDC hDC); void RenderString(TextInfo& ti, const wstring& text, HDC hDC);
void RenderString(TextInfo &ti, HDC hDC=0); void RenderString(TextInfo& ti, HDC hDC = 0);
void calcStringSize(TextInfo &ti, HDC hDC=0) const; void calcStringSize(TextInfo& ti, HDC hDC = 0) const;
void formatString(const TextInfo &ti, HDC hDC) const; void formatString(const TextInfo& ti, HDC hDC) const;
static wstring getTimerText(TextInfo *tit, DWORD T); static wstring getTimerText(TextInfo* tit, DWORD T);
static wstring getTimerText(int ZeroTime, int format); static wstring getTimerText(int ZeroTime, int format);
void fadeOut(string Id, int ms); void fadeOut(string Id, int ms);
void setWaitCursor(bool wait); void setWaitCursor(bool wait);
void setWindowTitle(const wstring &title); void setWindowTitle(const wstring& title);
bool selectFirstItem(const string &name); bool selectFirstItem(const string& name);
void removeString(string Id); void removeString(string Id);
void refresh() const; void refresh() const;
void refreshFast() const; void refreshFast() const;
@ -515,269 +544,295 @@ public:
void takeShownStringsSnapshot(); void takeShownStringsSnapshot();
void refreshSmartFromSnapshot(bool allowMoveOffset); void refreshSmartFromSnapshot(bool allowMoveOffset);
void dropLine(double lines=1){CurrentY+=int(lineHeight*lines); MaxY=max(MaxY, CurrentY);} void dropLine(double lines = 1) { CurrentY += int(lineHeight * lines); MaxY = max(MaxY, CurrentY); }
int getCX() const {return CurrentX;} int getCX() const { return CurrentX; }
int getCY() const {return CurrentY;} int getCY() const { return CurrentY; }
int getWidth() const {return MaxX;} int getWidth() const { return MaxX; }
int getHeight() const {return MaxY;} int getHeight() const { return MaxY; }
void getTargetDimension(int &x, int &y) const; void getTargetDimension(int& x, int& y) const;
pair<int, int> getPos() const { return make_pair(CurrentX, CurrentY); } pair<int, int> getPos() const { return make_pair(CurrentX, CurrentY); }
void setPos(const pair<int, int> &p) { CurrentX = p.first; CurrentY = p.second; } void setPos(const pair<int, int>& p) { CurrentX = p.first; CurrentY = p.second; }
void setCX(int cx){CurrentX=cx;} void setCX(int cx) { CurrentX = cx; }
void setCY(int cy){CurrentY=cy;} void setCY(int cy) { CurrentY = cy; }
int getLineHeight() const {return lineHeight;} int getLineHeight() const { return lineHeight; }
int getLineHeight(gdiFonts font, const wchar_t *face) const; int getLineHeight(gdiFonts font, const wchar_t* face) const;
BaseInfo *setInputFocus(const string &id, bool select=false); BaseInfo* setInputFocus(const string& id, bool select = false);
InputInfo *getInputFocus(); InputInfo* getInputFocus();
void enableInput(const char *id, bool acceptMissing = false, void enableInput(const char* id, bool acceptMissing = false,
int requireExtraMatch = -1) {setInputStatus(id, true, acceptMissing, requireExtraMatch);} int requireExtraMatch = -1) {
void disableInput(const char *id, bool acceptMissing = false, setInputStatus(id, true, acceptMissing, requireExtraMatch);
int requireExtraMatch = -1) {setInputStatus(id, false, acceptMissing, requireExtraMatch);} }
void setInputStatus(const char *id, bool status, bool acceptMissing = false, int requireExtraMatch = -1); void disableInput(const char* id, bool acceptMissing = false,
void setInputStatus(const string &id, bool status, bool acceptMissing = false, int requireExtraMatch = -1) int requireExtraMatch = -1) {
{setInputStatus(id.c_str(), status, acceptMissing, requireExtraMatch);} setInputStatus(id, false, acceptMissing, requireExtraMatch);
}
void setInputStatus(const char* id, bool status, bool acceptMissing = false, int requireExtraMatch = -1);
void setInputStatus(const string& id, bool status, bool acceptMissing = false, int requireExtraMatch = -1)
{
setInputStatus(id.c_str(), status, acceptMissing, requireExtraMatch);
}
void setTabStops(const string &Name, int t1, int t2=-1); void setTabStops(const string& name, int t1, int t2 = -1);
void setData(const string &id, DWORD data); void setData(const string& id, DWORD data);
void setData(const string &id, void *data); void setData(const string& id, void* data);
void setData(const string &id, const string &data); void setData(const string& id, const string& data);
void *getData(const string &id) const; void* getData(const string& id) const;
int getDataInt(const string &id) const { return int(size_t(getData(id))); } int getDataInt(const string& id) const { return int(size_t(getData(id))); }
bool getData(const string &id, string &out) const; bool getData(const string& id, string& out) const;
DWORD selectColor(wstring &def, DWORD input); DWORD selectColor(wstring& def, DWORD input);
void autoRefresh(bool flag) {manualUpdate = !flag;} void autoRefresh(bool flag) { manualUpdate = !flag; }
bool getData(const string &id, DWORD &data) const; bool getData(const string& id, DWORD& data) const;
bool hasData(const char *id) const; bool hasData(const char* id) const;
int getItemDataByName(const char *id, const char *name) const; int getItemDataByName(const char* id, const char* name) const;
bool selectItemByData(const char *id, int data); bool selectItemByData(const char* id, int data);
bool selectItemByIndex(const char *id, int index); bool selectItemByIndex(const char* id, int index);
void removeSelected(const char *id); void removeSelected(const char* id);
bool selectItemByData(const string &id, int data) { bool selectItemByData(const string& id, int data) {
return selectItemByData(id.c_str(), data); return selectItemByData(id.c_str(), data);
} }
enum AskAnswer {AnswerNo = 0, AnswerYes = 1, AnswerCancel = 2}; enum class AskAnswer { AnswerNo = 0, AnswerYes = 1, AnswerCancel = 2, AnswerOK = 3 };
bool ask(const wstring &s); bool ask(const wstring& s);
AskAnswer askCancel(const wstring &s); AskAnswer askOkCancel(const wstring& s);
AskAnswer askCancel(const wstring& s);
void alert(const string &msg) const; void alert(const string& msg) const;
void alert(const wstring &msg) const; void alert(const wstring& msg) const;
// Alert from main thread (via callback) // Alert from main thread (via callback)
void delayAlert(const wstring& msg); void delayAlert(const wstring& msg);
// Get and clear any delayed alert // Get and clear any delayed alert
wstring getDelayedAlert(); wstring getDelayedAlert();
void fillDown(){Direction=1;} void fillDown() { flowDirection = FlowDirection::Down; }
void fillRight(){Direction=0;} void fillRight() { flowDirection = FlowDirection::Right; }
void fillNone(){Direction=-1;} void fillNone() { flowDirection = FlowDirection::None; }
void newColumn(){CurrentY=START_YP; CurrentX=MaxX+10;}
void newRow(){CurrentY=MaxY; CurrentX=10;}
void pushX(){SX=CurrentX;} void newColumn() { CurrentY = START_YP; CurrentX = MaxX + 10; }
void pushY(){SY=CurrentY;} void newRow() { CurrentY = MaxY; CurrentX = 10; }
void popX(){CurrentX=SX;}
void popY(){CurrentY=SY;} void pushX() { SX = CurrentX; }
void pushY() { SY = CurrentY; }
void popX() { CurrentX = SX; }
void popY() { CurrentY = SY; }
bool updatePos(int x, int y, int width, int height); bool updatePos(int x, int y, int width, int height);
void adjustDimension(int width, int height); void adjustDimension(int width, int height);
/** Return a selected item*/ /** Return a selected item*/
bool getSelectedItem(const string &id, ListBoxInfo &lbi); bool getSelectedItem(const string& id, ListBoxInfo& lbi);
/** Get number of items in a list box.'*/ /** Get number of items in a list box.'*/
int getNumItems(const char *id); int getNumItems(const char* id);
/** Return the selected data in first, second indicates if data was available*/ /** Return the selected data in first, second indicates if data was available*/
pair<int, bool> getSelectedItem(const string &id); pair<int, bool> getSelectedItem(const string& id);
pair<int, bool> getSelectedItem(const char *id); pair<int, bool> getSelectedItem(const char* id);
bool addItem(const string &id, const wstring &text, size_t data = 0); bool addItem(const string& id, const wstring& text, size_t data = 0);
bool addItem(const string &id, const vector< pair<wstring, size_t> > &items); bool addItem(const string& id, const vector< pair<wstring, size_t> >& items);
void filterOnData(const string &id, const unordered_set<int> &filter);
bool clearList(const string &id); void filterOnData(const string& id, const unordered_set<int>& filter);
bool hasWidget(const string &id) const; bool clearList(const string& id);
const wstring &getText(const char *id, bool acceptMissing = false, int requireExtraMatch = -1) const; bool hasWidget(const string& id) const;
BaseInfo &getBaseInfo(const string &id) const { const wstring& getText(const char* id, bool acceptMissing = false, int requireExtraMatch = -1) const;
return getBaseInfo(id.c_str());
BaseInfo& getBaseInfo(const string& id, int requireExtraMatch = -1) const {
return getBaseInfo(id.c_str(), requireExtraMatch);
} }
BaseInfo &getBaseInfo(const char *id) const; BaseInfo& getBaseInfo(const char* id, int requireExtraMatch = -1) const;
BaseInfo &getBaseInfo(const wchar_t *id) const { BaseInfo& getBaseInfo(const wchar_t* id, int requireExtraMatch = -1) const {
return getBaseInfo(narrow(id).c_str()); return getBaseInfo(narrow(id).c_str(), requireExtraMatch);
} }
int getTextNo(const char *id, bool acceptMissing = false) const; int getTextNo(const char* id, bool acceptMissing = false) const;
int getTextNo(const string &id, bool acceptMissing = false) const int getTextNo(const string& id, bool acceptMissing = false) const
{return getTextNo(id.c_str(), acceptMissing);} {
return getTextNo(id.c_str(), acceptMissing);
}
const wstring &getText(const string &id, bool acceptMissing = false) const const wstring& getText(const string& id, bool acceptMissing = false) const
{return getText(id.c_str(), acceptMissing);} {
return getText(id.c_str(), acceptMissing);
}
// Insert text and notify "focusList" // Insert text and notify "focusList"
bool insertText(const string &id, const wstring &text); bool insertText(const string& id, const wstring& text);
int getFontHeight(int format, const wstring &fontFace) const; int getFontHeight(int format, const wstring& fontFace) const;
// The html version should be UTF-8. // The html version should be UTF-8.
void copyToClipboard(const string &html, void copyToClipboard(const string& html,
const wstring &txt) const; const wstring& txt) const;
BaseInfo *setTextTranslate(const char *id, const wstring &text, bool update=false); BaseInfo* setTextTranslate(const char* id, const wstring& text, bool update = false);
BaseInfo *setTextTranslate(const char *id, const wchar_t *text, bool update=false); BaseInfo* setTextTranslate(const char* id, const wchar_t* text, bool update = false);
BaseInfo *setTextTranslate(const string &id, const wstring &text, bool update=false); BaseInfo* setTextTranslate(const string& id, const wstring& text, bool update = false);
BaseInfo *setText(const char *id, const wstring &text, bool update=false, int requireExtraMatch = -1); BaseInfo* setText(const char* id, const wstring& text, bool update = false, int requireExtraMatch = -1);
BaseInfo *setText(const wchar_t *id, const wstring &text, bool update=false) { BaseInfo* setText(const wchar_t* id, const wstring& text, bool update = false) {
return setText(narrow(id), text, update); return setText(narrow(id), text, update);
} }
BaseInfo *setText(const char *id, int number, bool update=false); BaseInfo* setText(const char* id, int number, bool update = false);
BaseInfo *setText(const string &id, const wstring &text, bool update=false) BaseInfo* setText(const string& id, const wstring& text, bool update = false)
{return setText(id.c_str(), text, update);} {
BaseInfo *setTextZeroBlank(const char *id, int number, bool update=false); return setText(id.c_str(), text, update);
BaseInfo *setText(const string &id, int number, bool update=false) }
{return setText(id.c_str(), number, update);} BaseInfo* setTextZeroBlank(const char* id, int number, bool update = false);
BaseInfo* setText(const string& id, int number, bool update = false)
{
return setText(id.c_str(), number, update);
}
void clearPage(bool autoRefresh, bool keepToolbar = false); void clearPage(bool autoRefresh, bool keepToolbar = false);
void TabFocus(int direction=1); void TabFocus(int direction = 1);
void enter(); void enter();
void escape(); void escape();
bool upDown(int direction); bool upDown(int direction);
void keyCommand(KeyCommandCode code); void keyCommand(KeyCommandCode code);
LRESULT ProcessMsg(UINT iMessage, LPARAM lParam, WPARAM wParam); LRESULT ProcessMsg(UINT iMessage, LPARAM lParam, WPARAM wParam);
void setWindow(HWND hWnd){hWndTarget=hWnd;} void setWindow(HWND hWnd) { hWndTarget = hWnd; }
void scaleSize(double scale, bool allowSmallScale = false, bool doRefresh = true); enum class ScaleOperation {
NoRefresh,
Refresh,
NoUpdate
};
ButtonInfo &addButton(const string &id, const wstring &text, GUICALLBACK cb = 0, void scaleSize(double scale, bool allowSmallScale = false, ScaleOperation type = ScaleOperation::Refresh);
const wstring &tooltip = L"");
ButtonInfo &addButton(int x, int y, const string &id, const wstring &text, ButtonInfo& addButton(const string& id, const wstring& text, GUICALLBACK cb = nullptr,
GUICALLBACK cb = 0, const wstring &tooltop=L""); const wstring& tooltip = L"");
ButtonInfo &addButton(int x, int y, int w, const string &id, const wstring &text, ButtonInfo& addButton(int x, int y, const string& id, const wstring& text,
GUICALLBACK cb, const wstring &tooltop, bool absPos, bool hasState); GUICALLBACK cb = nullptr, const wstring& tooltop = L"");
ButtonInfo& addButton(int x, int y, int w, const string& id, const wstring& text,
GUICALLBACK cb, const wstring& tooltop, bool absPos, bool hasState);
ButtonInfo& addCheckbox(const string& id, const wstring& text, GUICALLBACK cb = nullptr,
bool Checked = true, const wstring& tooltip = L"");
ButtonInfo& addCheckbox(int x, int y, const string& id,
const wstring& text, GUICALLBACK cb = nullptr,
bool checked = true, const wstring& tooltop = L"", bool absPos = false);
ButtonInfo &addCheckbox(const string &id, const wstring &text, GUICALLBACK cb=0,
bool Checked=true, const wstring &tooltip = L"");
ButtonInfo &addCheckbox(int x, int y, const string &id,
const wstring &text, GUICALLBACK cb=0,
bool checked=true, const wstring &tooltop = L"", bool absPos=false);
/// XXX Temporary /// XXX Temporary
ButtonInfo &addButton(const string &id, const string &text, GUICALLBACK cb = 0, const string &tooltip=""); ButtonInfo& addButton(const string& id, const string& text, GUICALLBACK cb = nullptr, const string& tooltip = "");
ButtonInfo &addButton(int x, int y, const string &id, const string &text, ButtonInfo& addButton(int x, int y, const string& id, const string& text,
GUICALLBACK cb = 0, const string &tooltop=""); GUICALLBACK cb = nullptr, const string& tooltop = "");
ButtonInfo &addButton(int x, int y, int w, const string &id, const string &text, ButtonInfo& addButton(int x, int y, int w, const string& id, const string& text,
GUICALLBACK cb, const string &tooltop, bool AbsPos, bool hasState); GUICALLBACK cb, const string& tooltop, bool AbsPos, bool hasState);
ButtonInfo &addCheckbox(const string &id, const string &text, GUICALLBACK cb=0, bool Checked=true, const string &Help=""); ButtonInfo& addCheckbox(const string& id, const string& text, GUICALLBACK cb = nullptr, bool Checked = true, const string& Help = "");
ButtonInfo &addCheckbox(int x, int y, const string &id, const string &text, GUICALLBACK cb=0, bool Checked=true, const string &Help="", bool AbsPos=false); ButtonInfo& addCheckbox(int x, int y, const string& id, const string& text, GUICALLBACK cb = nullptr, bool Checked = true, const string& Help = "", bool AbsPos = false);
/// XXX /// XXX
bool isChecked(const string &id);
void check(const string &id, bool state, bool keepOriginalState = false);
bool isInputChanged(const string &exclude); bool isChecked(const string& id);
void check(const string& id, bool state, bool keepOriginalState = false);
bool isInputChanged(const string& exclude);
/** Get width of input widget with specified length (chars)*/ /** Get width of input widget with specified length (chars)*/
pair<int,int> getInputDimension(int length) const; pair<int, int> getInputDimension(int length) const;
InputInfo &addInput(const string &id, const wstring &text = L"", int length=16, GUICALLBACK cb=0, InputInfo& addInput(const string& id, const wstring& text = L"", int length = 16, GUICALLBACK cb = nullptr,
const wstring &Explanation = L"", const wstring &tooltip=L""); const wstring& Explanation = L"", const wstring& tooltip = L"");
InputInfo &addInput(int x, int y, const string &id, const wstring &text, int length, InputInfo& addInput(int x, int y, const string& id, const wstring& text, int length,
GUICALLBACK cb=0, const wstring &Explanation = L"", const wstring &tooltip=L""); GUICALLBACK cb = nullptr, const wstring& Explanation = L"", const wstring& tooltip = L"");
InputInfo *replaceSelection(const char *id, const wstring &text); InputInfo* replaceSelection(const char* id, const wstring& text);
InputInfo &addInputBox(const string &id, int width, int height, const wstring &text, InputInfo& addInputBox(const string& id, int width, int height, const wstring& text,
GUICALLBACK cb, const wstring &explanation); GUICALLBACK cb, const wstring& explanation);
InputInfo &addInputBox(const string &id, int x, int y, int width, int height, InputInfo& addInputBox(const string& id, int x, int y, int width, int height,
const wstring &text, GUICALLBACK cb, const wstring &explanation); const wstring& text, GUICALLBACK cb, const wstring& explanation);
ListBoxInfo &addListBox(const string &id, int width, int height, GUICALLBACK cb=0, const wstring &explanation=L"", const wstring &tooltip=L"", bool multiple=false); ListBoxInfo& addListBox(const string& id, int width, int height, GUICALLBACK cb = nullptr, const wstring& explanation = L"", const wstring& tooltip = L"", bool multiple = false);
ListBoxInfo &addListBox(int x, int y, const string &id, int width, int height, GUICALLBACK cb=0, const wstring &explanation=L"", const wstring &tooltip=L"", bool multiple=false); ListBoxInfo& addListBox(int x, int y, const string& id, int width, int height, GUICALLBACK cb = nullptr, const wstring& explanation = L"", const wstring& tooltip = L"", bool multiple = false);
ListBoxInfo &addSelection(const string &id, int width, int height, GUICALLBACK cb=0, const wstring &explanation=L"", const wstring &tooltip=L""); ListBoxInfo& addSelection(const string& id, int width, int height, GUICALLBACK cb = nullptr, const wstring& explanation = L"", const wstring& tooltip = L"");
ListBoxInfo &addSelection(int x, int y, const string &id, int width, int height, GUICALLBACK cb=0, const wstring &explanation=L"", const wstring &tooltip=L""); ListBoxInfo& addSelection(int x, int y, const string& id, int width, int height, GUICALLBACK cb = nullptr, const wstring& explanation = L"", const wstring& tooltip = L"");
ListBoxInfo &addCombo(const string &id, int width, int height, GUICALLBACK cb=0, const wstring &explanation=L"", const wstring &tooltip=L""); ListBoxInfo& addCombo(const string& id, int width, int height, GUICALLBACK cb = nullptr, const wstring& explanation = L"", const wstring& tooltip = L"");
ListBoxInfo &addCombo(int x, int y, const string &id, int width, int height, GUICALLBACK cb=0, const wstring &explanation=L"", const wstring &tooltip=L""); ListBoxInfo& addCombo(int x, int y, const string& id, int width, int height, GUICALLBACK cb = nullptr, const wstring& explanation = L"", const wstring& tooltip = L"");
// Grows a listbox, selection, combo in X-direction to fit current contents. Returns true if changed. // Grows a listbox, selection, combo in X-direction to fit current contents. Returns true if changed.
bool autoGrow(const char *id); bool autoGrow(const char* id);
void setListDescription(const wstring& desc);
void setListDescription(const wstring &desc);
// Wide versions // Wide versions
TextInfo &addString(const string &id, int format, const wstring &text, GUICALLBACK cb=0); TextInfo& addString(const string& id, int format, const wstring& text, GUICALLBACK cb = nullptr);
TextInfo &addString(const string &id, int yp, int xp, int format, const wstring &text, TextInfo& addString(const string& id, int yp, int xp, int format, const wstring& text,
int xlimit=0, GUICALLBACK cb=0, const wchar_t *fontFace = 0); int xlimit = 0, GUICALLBACK cb = nullptr, const wchar_t* fontFace = nullptr);
TextInfo &addString(const char *id, int format, const wstring &text, GUICALLBACK cb=0); TextInfo& addString(const char* id, int format, const wstring& text, GUICALLBACK cb = nullptr);
TextInfo &addString(const char *id, int yp, int xp, int format, const wstring &text, TextInfo& addString(const char* id, int yp, int xp, int format, const wstring& text,
int xlimit=0, GUICALLBACK cb=0, const wchar_t *fontFace = 0); int xlimit = 0, GUICALLBACK cb = nullptr, const wchar_t* fontFace = nullptr);
// Untranslated versions // Untranslated versions
TextInfo &addStringUT(int yp, int xp, int format, const wstring &text, TextInfo& addStringUT(int yp, int xp, int format, const wstring& text,
int xlimit=0, GUICALLBACK cb=0, const wchar_t *fontFace = 0); int xlimit = 0, GUICALLBACK cb = nullptr, const wchar_t* fontFace = nullptr);
TextInfo &addStringUT(int format, const wstring &text, GUICALLBACK cb=0); TextInfo& addStringUT(int format, const wstring& text, GUICALLBACK cb = nullptr);
// Temporary XXX // Temporary XXX
TextInfo &addString(const string &id, int format, const string &text, GUICALLBACK cb=0); TextInfo& addString(const string& id, int format, const string& text, GUICALLBACK cb = nullptr);
TextInfo &addString(const string &id, int yp, int xp, int format, const string &text, TextInfo& addString(const string& id, int yp, int xp, int format, const string& text,
int xlimit=0, GUICALLBACK cb=0, const wchar_t *fontFace = 0); int xlimit = 0, GUICALLBACK cb = nullptr, const wchar_t* fontFace = nullptr);
TextInfo &addString(const char *id, int format, const string &text, GUICALLBACK cb=0); TextInfo& addString(const char* id, int format, const string& text, GUICALLBACK cb = nullptr);
TextInfo &addString(const char *id, int yp, int xp, int format, const string &text, TextInfo& addString(const char* id, int yp, int xp, int format, const string& text,
int xlimit=0, GUICALLBACK cb=0, const wchar_t *fontFace = 0); int xlimit = 0, GUICALLBACK cb = nullptr, const wchar_t* fontFace = nullptr);
// Untranslated versions // Untranslated versions
TextInfo &addStringUT(int yp, int xp, int format, const string &text, TextInfo& addStringUT(int yp, int xp, int format, const string& text,
int xlimit=0, GUICALLBACK cb=0, const wchar_t *fontFace = 0); int xlimit = 0, GUICALLBACK cb = nullptr, const wchar_t* fontFace = nullptr);
TextInfo &addStringUT(int format, const string &text, GUICALLBACK cb=0); TextInfo& addStringUT(int format, const string& text, GUICALLBACK cb = nullptr);
// XXX Temporary // XXX Temporary
TextInfo &addTimer(int yp, int xp, int format, DWORD ZeroTime, TextInfo& addImage(const string& id, int yp, int xp, int format, const wstring& imageId,
int xlimit=0, GUICALLBACK cb=0, int TimeOut=NOTIMEOUT, const wchar_t *fontFace = 0); int width = 0, int height = 0, GUICALLBACK cb = nullptr);
TextInfo &addTimeout(int TimeOut, GUICALLBACK cb);
void removeTimeoutMilli(const string &id); TextInfo& addTimer(int yp, int xp, int format, DWORD ZeroTime,
TimerInfo &addTimeoutMilli(int timeOut, const string &id, GUICALLBACK cb); int xlimit = 0, GUICALLBACK cb = nullptr, int TimeOut = NOTIMEOUT, const wchar_t* fontFace = nullptr);
void timerProc(TimerInfo &timer, DWORD timeout);
void removeHandler(GuiHandler *h); TextInfo& addTimeout(int TimeOut, GUICALLBACK cb);
void draw(HDC hDC, RECT &windowArea, RECT &drawArea); void removeTimeoutMilli(const string& id);
TimerInfo& addTimeoutMilli(int timeOut, const string& id, GUICALLBACK cb);
void timerProc(TimerInfo& timer, DWORD timeout);
void removeHandler(GuiHandler* h);
void draw(HDC hDC, RECT& windowArea, RECT& drawArea);
void closeWindow(); void closeWindow();
int popupMenu(int x, int y, const vector<pair<wstring, int>> &menuItems) const; int popupMenu(int x, int y, const vector<pair<wstring, int>>& menuItems) const;
void setDBErrorState(bool state); void setDBErrorState(bool state);
friend class Table; friend class Table;
friend gdioutput *createExtraWindow(const string &tag, const wstring &title, int max_x, int max_y, bool fixedSize); friend gdioutput* createExtraWindow(const string& tag, const wstring& title, int max_x, int max_y, bool fixedSize);
gdioutput(const string &tag, double _scale); gdioutput(const string& tag, double _scale);
gdioutput(double _scale, HWND hWndTarget, const PrinterObject &defprn); gdioutput(double _scale, HWND hWndTarget, const PrinterObject& defprn);
virtual ~gdioutput(); virtual ~gdioutput();
}; };

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -82,6 +82,10 @@ public:
GuiHandler &getHandler() const; GuiHandler &getHandler() const;
BaseInfo &setHandler(const GuiHandler *h) {handler = const_cast<GuiHandler *>(h); return *this;} BaseInfo &setHandler(const GuiHandler *h) {handler = const_cast<GuiHandler *>(h); return *this;}
BaseInfo &setHandler(const shared_ptr<GuiHandler> &h) { managedHandler = h; return *this; } BaseInfo &setHandler(const shared_ptr<GuiHandler> &h) { managedHandler = h; return *this; }
void clearHandler() {
handler = nullptr;
managedHandler.reset();
}
}; };
class RestoreInfo : public BaseInfo class RestoreInfo : public BaseInfo
@ -108,6 +112,12 @@ public:
GUICALLBACK onClear; GUICALLBACK onClear;
GUICALLBACK postClear; GUICALLBACK postClear;
set<string> restorePoints;
bool operator<(const RestoreInfo &r) const {
return nLBI < r.nLBI || nBI < r.nBI || nII < r.nII || nTL < r.nTL || nRect < r.nRect || nData < r.nData;
}
HWND getControlWindow() const {throw std::exception("Unsupported");} HWND getControlWindow() const {throw std::exception("Unsupported");}
}; };
@ -387,11 +397,12 @@ private:
DWORD dataInt; DWORD dataInt;
wstring dataString; wstring dataString;
gdioutput *parent; gdioutput *parent;
TimerInfo(gdioutput *gdi, GUICALLBACK cb) : parent(gdi), callBack(cb), setWnd(0), timerId(++globalTimerId) {}
HWND setWnd; HWND setWnd;
public: public:
~TimerInfo(); ~TimerInfo();
TimerInfo(gdioutput* gdi, GUICALLBACK cb) : parent(gdi), callBack(cb), setWnd(0), timerId(++globalTimerId) {}
TimerInfo(const TimerInfo&) = delete;
TimerInfo& operator=(const TimerInfo&) = delete;
int getId() const { return timerId; } int getId() const { return timerId; }
BaseInfo &setExtra(const wchar_t *e) {return BaseInfo::setExtra(e);} BaseInfo &setExtra(const wchar_t *e) {return BaseInfo::setExtra(e);}
BaseInfo &setExtra(int e) {return BaseInfo::setExtra(e);} BaseInfo &setExtra(int e) {return BaseInfo::setExtra(e);}

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -266,7 +266,7 @@ template<class T> void GeneralResult::sort(vector<T *> &rt, SortOrder so) const
ps = None; ps = None;
vector< pair<int, oAbstractRunner *> > arr(rt.size()); vector< pair<int, oAbstractRunner *> > arr(rt.size());
const int maxT = 3600 * 100; const int maxT = timeConstHour * 100;
for (size_t k = 0; k < rt.size(); k++) { for (size_t k = 0; k < rt.size(); k++) {
arr[k].first = 0; arr[k].first = 0;
if (ps == ClassWise) { if (ps == ClassWise) {
@ -293,7 +293,7 @@ template<class T> void GeneralResult::sort(vector<T *> &rt, SortOrder so) const
ord = maxT - ord; ord = maxT - ord;
} }
else if (so == SortByStartTime || so == ClassStartTime || else if (so == SortByStartTime || so == ClassStartTime ||
so == ClassStartTimeClub) { so == ClassStartTimeClub || so == ClubClassStartTime) {
ord = tr.getStartTime(); ord = tr.getStartTime();
} }
@ -593,7 +593,7 @@ int TotalResultAtControl::deduceTime(oRunner &runner, int startTime) const {
pair<int,int> TotalResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { pair<int,int> TotalResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const {
if (asTeamMember) if (asTeamMember)
return make_pair(runner.getLegNumber(), 0); return make_pair(runner.getLegNumber(), 0);
const int TK = 3600 * 24 * 7; const int TK = timeConstHour * 24 * 7;
RunnerStatus inputStatus = StatusOK; RunnerStatus inputStatus = StatusOK;
if (runner.getTeam()) { if (runner.getTeam()) {
@ -781,7 +781,7 @@ RunnerStatus DynamicResult::toStatus(int status) const {
pair<int,int> DynamicResult::score(oTeam &team, RunnerStatus st, int time, int points) const { pair<int,int> DynamicResult::score(oTeam &team, RunnerStatus st, int time, int points) const {
if (getMethod(MTScore)) { if (getMethod(MTScore)) {
parser.addSymbol("ComputedTime", time); parser.addSymbol("ComputedTime", time / timeConstSecond);
parser.addSymbol("ComputedStatus", st); parser.addSymbol("ComputedStatus", st);
parser.addSymbol("ComputedPoints", points); parser.addSymbol("ComputedPoints", points);
return make_pair(0, getMethod(MTScore)->evaluate(parser)); return make_pair(0, getMethod(MTScore)->evaluate(parser));
@ -801,7 +801,7 @@ RunnerStatus DynamicResult::deduceStatus(oTeam &team) const {
int DynamicResult::deduceTime(oTeam &team) const { int DynamicResult::deduceTime(oTeam &team) const {
if (getMethod(MDeduceTTime)) if (getMethod(MDeduceTTime))
return getMethod(MDeduceTTime)->evaluate(parser); return getMethod(MDeduceTTime)->evaluate(parser) * timeConstSecond + team.getSubSeconds();
else if (getMethodSource(MDeduceTTime).empty()) else if (getMethodSource(MDeduceTTime).empty())
return GeneralResult::deduceTime(team); return GeneralResult::deduceTime(team);
else throw meosException("Syntax error"); else throw meosException("Syntax error");
@ -817,7 +817,7 @@ int DynamicResult::deducePoints(oTeam &team) const {
pair<int,int> DynamicResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { pair<int,int> DynamicResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const {
if (getMethod(MRScore)) { if (getMethod(MRScore)) {
parser.addSymbol("ComputedTime", time); parser.addSymbol("ComputedTime", time / timeConstSecond);
parser.addSymbol("ComputedStatus", st); parser.addSymbol("ComputedStatus", st);
parser.addSymbol("ComputedPoints", points); parser.addSymbol("ComputedPoints", points);
return make_pair(0, getMethod(MRScore)->evaluate(parser)); return make_pair(0, getMethod(MRScore)->evaluate(parser));
@ -837,7 +837,7 @@ RunnerStatus DynamicResult::deduceStatus(oRunner &runner) const {
int DynamicResult::deduceTime(oRunner &runner, int startTime) const { int DynamicResult::deduceTime(oRunner &runner, int startTime) const {
if (getMethod(MDeduceRTime)) if (getMethod(MDeduceRTime))
return getMethod(MDeduceRTime)->evaluate(parser); return getMethod(MDeduceRTime)->evaluate(parser) * timeConstSecond + runner.getSubSeconds();
else if (getMethodSource(MDeduceRTime).empty()) else if (getMethodSource(MDeduceRTime).empty())
return GeneralResult::deduceTime(runner, startTime); return GeneralResult::deduceTime(runner, startTime);
else throw meosException("Syntax error"); else throw meosException("Syntax error");
@ -1160,24 +1160,24 @@ void DynamicResult::prepareCommon(oAbstractRunner &runner, bool classResult) con
if (st == StatusUnknown && ft > 0) if (st == StatusUnknown && ft > 0)
st = StatusOK; st = StatusOK;
parser.addSymbol("Status", st); parser.addSymbol("Status", st);
parser.addSymbol("Start", runner.getStartTime()); parser.addSymbol("Start", runner.getStartTime() / timeConstSecond);
parser.addSymbol("Finish", ft); parser.addSymbol("Finish", ft / timeConstSecond);
parser.addSymbol("Time", runner.getRunningTime(useComputed)); parser.addSymbol("Time", runner.getRunningTime(useComputed) / timeConstSecond);
parser.addSymbol("Place", runner.getPlace(false)); parser.addSymbol("Place", runner.getPlace(false));
parser.addSymbol("Points", runner.getRogainingPoints(useComputed, false)); parser.addSymbol("Points", runner.getRogainingPoints(useComputed, false));
parser.addSymbol("PointReduction", runner.getRogainingReduction(useComputed)); parser.addSymbol("PointReduction", runner.getRogainingReduction(useComputed));
parser.addSymbol("PointOvertime", runner.getRogainingOvertime(useComputed)); parser.addSymbol("PointOvertime", runner.getRogainingOvertime(useComputed) / timeConstSecond);
parser.addSymbol("PointGross", runner.getRogainingPointsGross(useComputed)); parser.addSymbol("PointGross", runner.getRogainingPointsGross(useComputed));
parser.addSymbol("PointAdjustment", runner.getPointAdjustment()); parser.addSymbol("PointAdjustment", runner.getPointAdjustment());
parser.addSymbol("TimeAdjustment", runner.getTimeAdjustment()); parser.addSymbol("TimeAdjustment", runner.getTimeAdjustment(true) / timeConstSecond);
parser.addSymbol("TotalStatus", runner.getTotalStatus()); parser.addSymbol("TotalStatus", runner.getTotalStatus(false));
parser.addSymbol("TotalTime", runner.getTotalRunningTime()); parser.addSymbol("TotalTime", runner.getTotalRunningTime()/timeConstSecond);
parser.addSymbol("TotalPlace", runner.getTotalPlace(false)); parser.addSymbol("TotalPlace", runner.getTotalPlace(false));
parser.addSymbol("InputStatus", runner.getInputStatus()); parser.addSymbol("InputStatus", runner.getInputStatus());
parser.addSymbol("InputTime", runner.getInputTime()); parser.addSymbol("InputTime", runner.getInputTime() / timeConstSecond);
parser.addSymbol("InputPlace", runner.getInputPlace()); parser.addSymbol("InputPlace", runner.getInputPlace());
parser.addSymbol("InputPoints", runner.getInputPoints()); parser.addSymbol("InputPoints", runner.getInputPoints());
parser.addSymbol("Shorten", runner.getNumShortening()); parser.addSymbol("Shorten", runner.getNumShortening());
@ -1191,13 +1191,16 @@ void DynamicResult::prepareCommon(oAbstractRunner &runner, bool classResult) con
for (RunnerStatus s : inst) for (RunnerStatus s : inst)
iinst.push_back(s); iinst.push_back(s);
for (int &t : times)
t /= timeConstSecond;
parser.addSymbol("StageStatus", iinst); parser.addSymbol("StageStatus", iinst);
parser.addSymbol("StageTime", times); parser.addSymbol("StageTime", times);
parser.addSymbol("StagePlace", places); parser.addSymbol("StagePlace", places);
parser.addSymbol("StagePoints", points); parser.addSymbol("StagePoints", points);
parser.addSymbol("InputStatus", runner.getInputStatus()); parser.addSymbol("InputStatus", runner.getInputStatus());
parser.addSymbol("InputTime", runner.getInputTime()); parser.addSymbol("InputTime", runner.getInputTime() / timeConstSecond);
parser.addSymbol("InputPlace", runner.getInputPlace()); parser.addSymbol("InputPlace", runner.getInputPlace());
parser.addSymbol("InputPoints", runner.getInputPoints()); parser.addSymbol("InputPoints", runner.getInputPoints());
@ -1226,13 +1229,13 @@ void DynamicResult::prepareCalculations(oTeam &team, bool classResult) const {
for (int k = 0; k < nr; k++) { for (int k = 0; k < nr; k++) {
pRunner r = team.getRunner(k); pRunner r = team.getRunner(k);
if (r) { if (r) {
oAbstractRunner::TempResult &res = r->getTempResult(); const oAbstractRunner::TempResult &res = r->getTempResult();
status[k] = res.getStatus(); status[k] = res.getStatus();
time[k] = res.getRunningTime(); time[k] = res.getRunningTime() / timeConstSecond;
if (time[k] > 0 && status[k] == StatusUnknown) if (time[k] > 0 && status[k] == StatusUnknown)
status[k] = StatusOK; status[k] = StatusOK;
start[k] = res.getStartTime(); start[k] = res.getStartTime() / timeConstSecond;
finish[k] = res.getFinishTime(); finish[k] = res.getFinishTime() / timeConstSecond;
points[k] = res.getPoints(); points[k] = res.getPoints();
if (classResult) { if (classResult) {
r->updateComputedResultFromTemp(); r->updateComputedResultFromTemp();
@ -1281,7 +1284,7 @@ void DynamicResult::prepareCalculations(oTeam &team, bool classResult) const {
pClass cls = team.getClassRef(true); pClass cls = team.getClassRef(true);
if (cls) { if (cls) {
int nl = max<int>(1, cls->getNumStages() - 1); int nl = max<int>(1, cls->getNumStages() - 1);
parser.addSymbol("ShortestClassTime", cls->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, nl, false, false)); parser.addSymbol("ShortestClassTime", cls->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, nl, false, false) / timeConstSecond);
} }
} }
@ -1303,7 +1306,7 @@ void DynamicResult::prepareCalculations(oRunner &runner, bool classResult) const
int ip = 0; int ip = 0;
for (size_t k = 0; k < punches.size(); k++) { for (size_t k = 0; k < punches.size(); k++) {
if (punches[k]->getTypeCode() >= 30) { if (punches[k]->getTypeCode() >= 30) {
times[ip] = punches[k]->getAdjustedTime(); times[ip] = punches[k]->getAdjustedTime() / timeConstSecond;
codes[ip] = punches[k]->getTypeCode(); codes[ip] = punches[k]->getTypeCode();
controls[ip] = punches[k]->isUsedInCourse() ? punches[k]->getControlId() : -1; controls[ip] = punches[k]->isUsedInCourse() ? punches[k]->getControlId() : -1;
ip++; ip++;
@ -1345,16 +1348,16 @@ void DynamicResult::prepareCalculations(oRunner &runner, bool classResult) const
if (ctrl->isSingleStatusOK()) { if (ctrl->isSingleStatusOK()) {
eCrs.push_back(ctrl->getFirstNumber()); eCrs.push_back(ctrl->getFirstNumber());
if (size_t(k) < sp.size()) { if (size_t(k) < sp.size()) {
if (sp[k].status == SplitData::OK) { if (sp[k].hasTime()) {
eAccTime.push_back(sp[k].time - start); eAccTime.push_back((sp[k].getTime(false) - start) / timeConstSecond);
eSplitTime.push_back(sp[k].time - st); eSplitTime.push_back((sp[k].getTime(false) - st) / timeConstSecond);
st = sp[k].time; st = sp[k].getTime(false);
} }
else if (sp[k].status == SplitData::NoTime) { else if (sp[k].getStatus() == SplitData::SplitStatus::NoTime) {
eAccTime.push_back(st - start); eAccTime.push_back( (st - start) / timeConstSecond);
eSplitTime.push_back(0); eSplitTime.push_back(0);
} }
else if (sp[k].status == SplitData::Missing) { else if (sp[k].isMissing()) {
eAccTime.push_back(0); eAccTime.push_back(0);
eSplitTime.push_back(-1); eSplitTime.push_back(-1);
} }
@ -1362,8 +1365,8 @@ void DynamicResult::prepareCalculations(oRunner &runner, bool classResult) const
} }
} }
if (runner.getFinishTime() > 0) { if (runner.getFinishTime() > 0) {
eAccTime.push_back(runner.getFinishTime() - start); eAccTime.push_back((runner.getFinishTime() - start) / timeConstSecond);
eSplitTime.push_back(runner.getFinishTime() - st); eSplitTime.push_back((runner.getFinishTime() - st) / timeConstSecond);
} }
else if (!eAccTime.empty()) { else if (!eAccTime.empty()) {
eAccTime.push_back(0); eAccTime.push_back(0);
@ -1394,7 +1397,7 @@ void DynamicResult::prepareCalculations(oRunner &runner, bool classResult) const
pClass cls = runner.getClassRef(true); pClass cls = runner.getClassRef(true);
if (cls) { if (cls) {
int nl = runner.getLegNumber(); int nl = runner.getLegNumber();
parser.addSymbol("ShortestClassTime", cls->getBestLegTime(oClass::AllowRecompute::Yes, nl, false)); parser.addSymbol("ShortestClassTime", cls->getBestLegTime(oClass::AllowRecompute::Yes, nl, false) / timeConstSecond);
} }
vector<int> delta; vector<int> delta;
vector<int> place; vector<int> place;
@ -1523,7 +1526,7 @@ void GeneralResult::calculateIndividualResults(vector<pRunner> &runners,
else { else {
oe.calculateResults(set<int>(), oEvent::ResultType::TotalResult, inclPreliminary); oe.calculateResults(set<int>(), oEvent::ResultType::TotalResult, inclPreliminary);
for (pRunner r : runners) { for (pRunner r : runners) {
ri.status = r->getTotalStatus(); ri.status = r->getTotalStatus(false);
if (ri.status == StatusUnknown && r->getInputStatus() == StatusOK) { if (ri.status == StatusUnknown && r->getInputStatus() == StatusOK) {
if (r->getFinishTime() == 0) { if (r->getFinishTime() == 0) {
if (!inclForestRunners) if (!inclForestRunners)

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -89,9 +89,9 @@ vector<uint8_t> Image::loadResourceToMemory(LPCTSTR lpName, LPCTSTR lpType) {
return result; return result;
} }
HBITMAP Image::read_png(vector<uint8_t> &inData, int &width, int &height, ImageMethod method) { HBITMAP Image::read_png(vector<uint8_t> &&inData, int &width, int &height, ImageMethod method) {
PngData inputStream; PngData inputStream;
inputStream.memory.swap(inData); inputStream.memory = std::move(inData);
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) if (!png)
return nullptr; return nullptr;
@ -196,25 +196,57 @@ HBITMAP Image::read_png(vector<uint8_t> &inData, int &width, int &height, ImageM
row[x + 3] = src[x + 3]; row[x + 3] = src[x + 3];
} }
} }
else if (method == ImageMethod::WhiteTransparent) {
for (size_t x = 0; x < cbStride; x += 4) {
row[x + 2] = src[x + 0]; // Red
row[x + 1] = src[x + 1]; // Green
row[x + 0] = src[x + 2]; // Blue
row[x + 3] = src[x + 3];
if (src[x + 0] == 0xFF && src[x + 1] == 0xFF && src[x + 2] == 0xFF) {
row[x + 3] = 0;
row[x + 2] = 0;
row[x + 1] = 0;
row[x + 0] = 0;
}
}
}
} }
return hbmp; return hbmp;
} }
HBITMAP Image::read_png_file(const wstring &filename, int &width, int &height, ImageMethod method) { uint64_t Image::computeHash(const vector<uint8_t>& data) {
width = 0; uint64_t h = data.size();
height = 0; size_t siz4 = data.size() / 4;
PngData inputStream; const uint32_t* ptr = (const uint32_t *)data.data();
inputStream.memory; size_t lim = siz4 * 4;
for (size_t e = siz4 * 4; e < data.size(); e++)
h = h * 256 + data[e];
for (size_t e = 0; e < siz4; e++) {
h = 997 * h + ptr[e];
}
return h;
}
void Image::read_file(const wstring& filename, vector<uint8_t>& data) {
ifstream fin; ifstream fin;
fin.open(filename, ios::binary); fin.open(filename, ios::binary);
fin.seekg(0, ios::end); fin.seekg(0, ios::end);
int p2 = (int)fin.tellg(); int p2 = (int)fin.tellg();
fin.seekg(0, ios::beg); fin.seekg(0, ios::beg);
inputStream.memory.resize(p2); data.resize(p2);
fin.read((char *)&inputStream.memory[0], inputStream.memory.size()); fin.read((char*)data.data(), data.size());
fin.close(); fin.close();
return read_png(inputStream.memory, width, height, method); }
HBITMAP Image::read_png_file(const wstring &filename, int &width, int &height, uint64_t &hash, ImageMethod method) {
width = 0;
height = 0;
PngData inputStream;
read_file(filename, inputStream.memory);
hash = computeHash(inputStream.memory);
return read_png(std::move(inputStream.memory), width, height, method);
} }
HBITMAP Image::read_png_resource(LPCTSTR lpName, LPCTSTR lpType, int &width, int &height, ImageMethod method) { HBITMAP Image::read_png_resource(LPCTSTR lpName, LPCTSTR lpType, int &width, int &height, ImageMethod method) {
@ -224,7 +256,7 @@ HBITMAP Image::read_png_resource(LPCTSTR lpName, LPCTSTR lpType, int &width, int
inputStream.memory = loadResourceToMemory(lpName, lpType); inputStream.memory = loadResourceToMemory(lpName, lpType);
if (inputStream.memory.empty()) if (inputStream.memory.empty())
return nullptr; return nullptr;
return read_png(inputStream.memory, width, height, method); return read_png(std::move(inputStream.memory), width, height, method);
} }
Image::Image() Image::Image()
@ -236,7 +268,7 @@ Image::~Image()
} }
// Loads the PNG containing the splash image into a HBITMAP. // Loads the PNG containing the splash image into a HBITMAP.
HBITMAP Image::loadImage(int resource, ImageMethod method) { HBITMAP Image::loadImage(uint64_t resource, ImageMethod method) {
if (images.count(resource)) if (images.count(resource))
return images[resource].image; return images[resource].image;
@ -250,18 +282,22 @@ HBITMAP Image::loadImage(int resource, ImageMethod method) {
return hbmp; return hbmp;
} }
int Image::getWidth(int resource) { int Image::getWidth(uint64_t resource) {
loadImage(resource, ImageMethod::Default); loadImage(resource, ImageMethod::Default);
return images[resource].width; return images[resource].width;
} }
int Image::getHeight(int resource) { int Image::getHeight(uint64_t resource) {
loadImage(resource, ImageMethod::Default); loadImage(resource, ImageMethod::Default);
return images[resource].height; return images[resource].height;
} }
void Image::drawImage(int resource, ImageMethod method, HDC hDC, int x, int y, int width, int height) { void Image::drawImage(uint64_t resource, ImageMethod method, HDC hDC, int x, int y, int width, int height) {
HBITMAP bmp = loadImage(resource, method); HBITMAP bmp = loadImage(resource, method);
auto res = images.find(resource);
if (res == images.end())
return;
HDC memdc = CreateCompatibleDC(hDC); HDC memdc = CreateCompatibleDC(hDC);
SelectObject(memdc, bmp); SelectObject(memdc, bmp);
@ -270,7 +306,130 @@ void Image::drawImage(int resource, ImageMethod method, HDC hDC, int x, int y, i
bf.BlendFlags = 0; bf.BlendFlags = 0;
bf.SourceConstantAlpha =0xFF; bf.SourceConstantAlpha =0xFF;
bf.AlphaFormat = AC_SRC_ALPHA; bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hDC, x, y, width, height, memdc, 0, 0, width, height, bf); AlphaBlend(hDC, x, y, width, height, memdc, 0, 0, res->second.width, res->second.height, bf);
DeleteDC(memdc); DeleteDC(memdc);
} }
uint64_t Image::loadFromFile(const wstring& path, ImageMethod method) {
vector<uint8_t> bytes;
read_file(path, bytes);
uint64_t hash = computeHash(bytes);
auto res = images.emplace(hash, Bmp());
if (res.second) {
wchar_t drive[20];
wchar_t dir[MAX_PATH];
wchar_t name[MAX_PATH];
wchar_t ext[MAX_PATH];
_wsplitpath_s(path.c_str(), drive, dir, name, ext);
Bmp &out = res.first->second;
out.fileName = wstring(name) + ext;
out.rawData = bytes;
out.image = read_png(std::move(bytes), out.width, out.height, method);
}
return hash;
}
uint64_t Image::loadFromMemory(const wstring& fileName, const vector<uint8_t>& bytes, ImageMethod method) {
uint64_t hash = computeHash(bytes);
return hash;
}
void Image::provideFromMemory(uint64_t id, const wstring& fileName, const vector<uint8_t>& bytes) {
uint64_t hash = computeHash(bytes);
if (id != hash)
throw meosException(L"Corrupted image: " + fileName);
images[id].fileName = fileName;
images[id].rawData = bytes;
}
void Image::addImage(uint64_t id, const wstring& fileName) {
images[id].fileName = fileName;
}
void Image::reloadImage(uint64_t imgId, ImageMethod method) {
auto res = images.find(imgId);
if (res != images.end() && res->second.rawData.size() > 0) {
auto copy = res->second.rawData;
res->second.destroy();
res->second.image = read_png(std::move(copy), res->second.width, res->second.height, method);
return;
}
throw meosException("Unknown image " + itos(imgId));
}
void Image::clearLoaded() {
for (auto iter = images.begin(); iter != images.end(); ) {
if (iter->second.fileName.empty())
++iter;
else {
iter = images.erase(iter);
}
}
}
Image::Bmp::~Bmp() {
destroy();
}
void Image::Bmp::destroy() {
if (image) {
DeleteObject(image);
image = nullptr;
}
}
void Image::enumerateImages(vector<pair<wstring, size_t>>& img) const {
img.clear();
for (auto& bmp : images) {
if (bmp.second.fileName.size() > 0)
img.emplace_back(bmp.second.fileName, img.size());
}
sort(img.begin(), img.end());
}
uint64_t Image::getIdFromEnumeration(int enumerationIx) const {
int ix = 0;
for (auto& bmp : images) {
if (bmp.second.fileName.size() > 0) {
if (enumerationIx == ix++) {
return bmp.first;
}
}
}
throw meosException("Internal error");
}
int Image::getEnumerationIxFromId(uint64_t imgId) const {
int ix = 0;
for (auto& bmp : images) {
if (bmp.second.fileName.size() > 0) {
if (imgId == bmp.first)
return ix;
ix++;
}
}
return -1;
}
const wstring& Image::getFileName(uint64_t imgId) const {
if (!hasImage(imgId))
throw meosException("Missing image: " + itos(imgId));
return images.at(imgId).fileName;
}
const vector<uint8_t>& Image::getRawData(uint64_t imgId) const {
if (!hasImage(imgId))
throw meosException("Missing image: " + itos(imgId));
return images.at(imgId).rawData;
}
bool Image::hasImage(uint64_t imgId) const {
auto res = images.find(imgId);
return res != images.end() && res->second.rawData.size() > 0;
}

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -29,34 +29,57 @@ class Image {
public: public:
enum class ImageMethod { enum class ImageMethod {
Default, Default,
MonoAlpha MonoAlpha,
WhiteTransparent
}; };
private: private:
static HBITMAP read_png_file(const wstring &filename, int &width, int &height, ImageMethod method); static uint64_t computeHash(const vector<uint8_t>& data);
static void read_file(const wstring& filename, vector<uint8_t>& data);
static HBITMAP read_png_file(const wstring &filename, int &width, int &height, uint64_t &hash, ImageMethod method);
static HBITMAP read_png_resource(LPCTSTR lpName, LPCTSTR lpType, int &width, int &height, ImageMethod method); static HBITMAP read_png_resource(LPCTSTR lpName, LPCTSTR lpType, int &width, int &height, ImageMethod method);
static HBITMAP read_png(vector<uint8_t> &data, int &width, int &height, ImageMethod method); static HBITMAP read_png(vector<uint8_t> &&data, int &width, int &height, ImageMethod method);
struct Bmp { struct Bmp {
HBITMAP image; HBITMAP image = nullptr;
int width; int width = -1;
int height; int height = -1;
wstring fileName;
vector<uint8_t> rawData;
~Bmp();
void destroy();
}; };
map<int, Bmp> images; map<uint64_t, Bmp> images;
public: public:
HBITMAP loadImage(int resource, ImageMethod method); HBITMAP loadImage(uint64_t resource, ImageMethod method);
static vector<uint8_t> loadResourceToMemory(LPCTSTR lpName, LPCTSTR lpType); static vector<uint8_t> loadResourceToMemory(LPCTSTR lpName, LPCTSTR lpType);
int getWidth(int resource); int getWidth(uint64_t resource);
int getHeight(int resource); int getHeight(uint64_t resource);
void drawImage(int resource, ImageMethod method, HDC hDC, int x, int y, int width, int height); void drawImage(uint64_t resource, ImageMethod method, HDC hDC, int x, int y, int width, int height);
uint64_t loadFromFile(const wstring& path, ImageMethod method);
uint64_t loadFromMemory(const wstring& fileName, const vector<uint8_t> &bytes, ImageMethod method);
void provideFromMemory(uint64_t id, const wstring& fileName, const vector<uint8_t>& bytes);
void addImage(uint64_t id, const wstring& fileName);
void clearLoaded();
void enumerateImages(vector<pair<wstring, size_t>>& img) const;
uint64_t getIdFromEnumeration(int enumerationIx) const;
int getEnumerationIxFromId(uint64_t imgId) const;
bool hasImage(uint64_t imgId) const;
void reloadImage(uint64_t imgId, ImageMethod method);
const wstring& getFileName(uint64_t imgId) const;
const vector<uint8_t> &getRawData(uint64_t imgId) const;
Image(); Image();
~Image(); ~Image();
}; };

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

BIN
code/info24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -501,9 +501,9 @@ bool InfoBaseCompetitor::synchronizeBase(oAbstractRunner &bc) {
ch = true; ch = true;
} }
RunnerStatus s = bc.getStatusComputed(); RunnerStatus s = bc.getStatusComputed(true);
int rt = bc.getRunningTime(true) * 10; int rt = bc.getRunningTime(true) * (10/timeConstSecond);
if (rt > 0) { if (rt > 0) {
if (s == RunnerStatus::StatusUnknown) if (s == RunnerStatus::StatusUnknown)
s = RunnerStatus::StatusOK; s = RunnerStatus::StatusOK;
@ -521,7 +521,7 @@ bool InfoBaseCompetitor::synchronizeBase(oAbstractRunner &bc) {
int st = -1; int st = -1;
if (bc.startTimeAvailable()) if (bc.startTimeAvailable())
st = convertRelativeTime(bc, bc.getStartTime()) * 10; st = convertRelativeTime(bc, bc.getStartTime()) * (10 / timeConstSecond);
if (st != startTime) { if (st != startTime) {
startTime = st; startTime = st;
@ -564,7 +564,7 @@ bool InfoCompetitor::synchronize(bool useTotalResults, bool useCourse, oRunner &
pTeam t = r.getTeam(); pTeam t = r.getTeam();
if (useTotalResults) { if (useTotalResults) {
legInput = r.getTotalTimeInput() * 10; legInput = r.getTotalTimeInput() * (10 / timeConstSecond);
s = r.getTotalStatusInput(); s = r.getTotalStatusInput();
} }
else if (t && !isQF && r.getLegNumber() > 0) { else if (t && !isQF && r.getLegNumber() > 0) {
@ -578,7 +578,7 @@ bool InfoCompetitor::synchronize(bool useTotalResults, bool useCourse, oRunner &
} }
} }
if (ltu > 0) { if (ltu > 0) {
legInput = t->getLegRunningTime(ltu - 1, true, false) * 10; legInput = t->getLegRunningTime(ltu - 1, true, false) * (10 / timeConstSecond);
s = t->getLegStatus(ltu - 1, true, false); s = t->getLegStatus(ltu - 1, true, false);
} }
} }
@ -625,7 +625,7 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) {
} }
vector<RadioTime> newRT; vector<RadioTime> newRT;
if (r.getClassId(false) > 0 && r.getStatusComputed() != RunnerStatus::StatusNoTiming) { if (r.getClassId(false) > 0 && r.getStatusComputed(true) != RunnerStatus::StatusNoTiming) {
const vector<int> &radios = cmp.getControls(r.getClassId(true), r.getLegNumber()); const vector<int> &radios = cmp.getControls(r.getClassId(true), r.getLegNumber());
for (size_t k = 0; k < radios.size(); k++) { for (size_t k = 0; k < radios.size(); k++) {
RadioTime radioTime; RadioTime radioTime;
@ -634,7 +634,7 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) {
r.getSplitTime(radioTime.radioId, s_split, radioTime.runningTime); r.getSplitTime(radioTime.radioId, s_split, radioTime.runningTime);
if (radioTime.runningTime > 0) { if (radioTime.runningTime > 0) {
radioTime.runningTime*=10; radioTime.runningTime *= (10 / timeConstSecond);
newRT.push_back(radioTime); newRT.push_back(radioTime);
} }
} }

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -224,8 +224,8 @@ private:
protected: protected:
bool forceComplete; bool forceComplete;
bool includeTotal; bool includeTotal = false;
bool withCourse; bool withCourse = false;
list<InfoBase *> toCommit; list<InfoBase *> toCommit;

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -54,9 +54,9 @@ vector<int> parseSGTimes(const oEvent &oe, const wstring &name) {
return times; return times;
} }
IOF30Interface::IOF30Interface(oEvent *oe, bool forceSplitFee) : oe(*oe), useGMT(false), teamsAsIndividual(false), IOF30Interface::IOF30Interface(oEvent *oe, bool forceSplitFee, bool useEventorQuirks) : oe(*oe), useGMT(false), teamsAsIndividual(false),
entrySourceId(1), unrollLoops(true), entrySourceId(1), unrollLoops(true),
includeStageRaceInfo(true) { includeStageRaceInfo(true), useEventorQuirks(useEventorQuirks) {
cachedStageNumber = -1; cachedStageNumber = -1;
splitLateFee = forceSplitFee || oe->getPropertyInt("SplitLateFees", false) == 1; splitLateFee = forceSplitFee || oe->getPropertyInt("SplitLateFees", false) == 1;
} }
@ -705,7 +705,7 @@ void IOF30Interface::assignTeamCourse(gdioutput &gdi, oTeam *team, int iClass, i
if (team) { if (team) {
pRunner r = team->getRunner(legId); pRunner r = team->getRunner(legId);
if (r == 0) { if (r == 0) {
r = oe.addRunner(lang.tl(L"N.N."), team->getClubId(), team->getClassId(false), 0, 0, false); r = oe.addRunner(lang.tl(L"N.N."), team->getClubId(), team->getClassId(false), 0, L"", false);
if (r) { if (r) {
r->setEntrySource(entrySourceId); r->setEntrySource(entrySourceId);
r->flagEntryTouched(true); r->flagEntryTouched(true);
@ -959,7 +959,8 @@ void IOF30Interface::prescanCompetitorList(xmlobject &xo) {
} }
} }
void IOF30Interface::readCompetitorList(gdioutput &gdi, const xmlobject &xo, int &personCount) { void IOF30Interface::readCompetitorList(gdioutput &gdi, const xmlobject &xo,
bool onlyWithClub, int &personCount, int &duplicateCount) {
if (!xo) if (!xo)
return; return;
@ -971,11 +972,10 @@ void IOF30Interface::readCompetitorList(gdioutput &gdi, const xmlobject &xo, int
xmlList xl; xmlList xl;
xo.getObjects(xl); xo.getObjects(xl);
xmlList::const_iterator it; unordered_multimap<size_t, int> duplicateCheck;
for (auto &it : xl) {
for (it = xl.begin(); it != xl.end(); ++it) { if (it.is("Competitor")) {
if (it->is("Competitor")) { if (readXMLCompetitorDB(it, onlyWithClub, duplicateCheck, duplicateCount))
if (readXMLCompetitorDB(*it))
personCount++; personCount++;
} }
} }
@ -1409,12 +1409,17 @@ void IOF30Interface::readStartList(gdioutput &gdi, xmlobject &xo, int &entRead,
if (raceToInfo.size() == 1) { if (raceToInfo.size() == 1) {
RaceInfo &raceInfo = raceToInfo.begin()->second; RaceInfo &raceInfo = raceToInfo.begin()->second;
if (raceInfo.courseId > 0) { if (raceInfo.courseId > 0) {
if (pc->getCourse() == 0) { if (pc->getCourse() == nullptr) {
pCourse crs = oe.addCourse(pc->getName(), raceInfo.length, raceInfo.courseId); pCourse crs = raceInfo.courseId > 0 ? oe.getCourse(raceInfo.courseId) : nullptr;
crs->setStart(raceInfo.startName, false); if (crs == nullptr)
crs->getDI().setInt("Climb", raceInfo.climb); crs = oe.addCourse(pc->getName(), raceInfo.length, raceInfo.courseId);
pc->setCourse(crs);
crs->synchronize(); if (crs != nullptr) {
crs->setStart(raceInfo.startName, false);
crs->getDI().setInt("Climb", raceInfo.climb);
pc->setCourse(crs);
crs->synchronize();
}
} }
} }
} }
@ -1639,12 +1644,12 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo,
timeStr.swap(tTime); timeStr.swap(tTime);
int t = convertAbsoluteTimeISO(timeStr); int t = convertAbsoluteTimeISO(timeStr);
if (t >= 0 && oe.getNumRunners() == 0) { if (t >= 0 && oe.getNumRunners() == 0) {
int zt = t - 3600; int zt = t - timeConstHour;
if (zt < 0) if (zt < 0)
zt += 3600*24; zt += timeConstHour *24;
if (!oe.hasFlag(oEvent::TransferFlags::FlagManualDateTime)) if (!oe.hasFlag(oEvent::TransferFlags::FlagManualDateTime))
oe.setZeroTime(formatTimeHMS(zt), false); oe.setZeroTime(formatTimeHMS(zt, SubSecond::Auto), false);
} }
} }
if (!oe.hasFlag(oEvent::TransferFlags::FlagManualDateTime)) if (!oe.hasFlag(oEvent::TransferFlags::FlagManualDateTime))
@ -1762,7 +1767,7 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo,
xmlList nameList; xmlList nameList;
s.getObjects("Name", nameList); s.getObjects("Name", nameList);
for (auto s : nameList) { for (auto s : nameList) {
services.emplace_back(id, s.getw()); services.emplace_back(id, s.getWStr());
} }
} }
} }
@ -2079,7 +2084,7 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea
if (cardNo > 0 && r == 0 && team) { if (cardNo > 0 && r == 0 && team) {
// We got no person, but a card number. Add the runner anonymously. // We got no person, but a card number. Add the runner anonymously.
r = oe.addRunner(lang.tl(L"N.N."), team->getClubId(), team->getClassId(false), cardNo, 0, false); r = oe.addRunner(lang.tl(L"N.N."), team->getClubId(), team->getClassId(false), cardNo, L"", false);
r->flagEntryTouched(true); r->flagEntryTouched(true);
r->setEntrySource(entrySourceId); r->setEntrySource(entrySourceId);
r->synchronize(); r->synchronize();
@ -2197,7 +2202,7 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea
if (ts >= 2 && times[ts - 2] < times[ts - 1]) if (ts >= 2 && times[ts - 2] < times[ts - 1])
oe.setStartGroup(groupId, times[ts - 2], times[ts - 1], groupName); oe.setStartGroup(groupId, times[ts - 2], times[ts - 1], groupName);
else else
oe.setStartGroup(groupId, 3600, 3600 * 2, groupName); oe.setStartGroup(groupId, timeConstHour, timeConstHour * 2, groupName);
} }
r->setStartGroup(groupId); r->setStartGroup(groupId);
} }
@ -2415,14 +2420,14 @@ pRunner IOF30Interface::readPersonResult(gdioutput &gdi, pClass pc, xmlobject &x
int time = split.getObjectInt("Time"); int time = split.getObjectInt("Time");
split.getObjectString("status", s); split.getObjectString("status", s);
if (s != L"missing") if (s != L"missing")
card->addPunch(code, st + time, 0); card->addPunch(code, st + time, 0, 0);
if (s != L"additional") if (s != L"additional")
controls.push_back(code); controls.push_back(code);
} }
if (ft > 0) if (ft > 0)
card->addPunch(oPunch::PunchFinish, ft, 0); card->addPunch(oPunch::PunchFinish, ft, 0, 0);
//Update to SQL-source //Update to SQL-source
card->synchronize(); card->synchronize();
@ -2469,11 +2474,11 @@ void IOF30Interface::readId(const xmlobject &person, int &pid, __int64 &extId) c
person.getObjects("Id", sids); person.getObjects("Id", sids);
for (auto &x : sids) { for (auto &x : sids) {
auto type = x.getAttrib("type"); auto type = x.getAttrib("type");
if (type && type.get() == preferredIdProvider) { if (type && type.getPtr() == preferredIdProvider) {
sid = x.getw(); sid = x.getWStr();
} }
else if (bsid.empty()) else if (bsid.empty())
bsid = x.getw(); bsid = x.getWStr();
} }
if (sid.empty()) if (sid.empty())
pid = oBase::idFromExtId(oBase::converExtIdentifierString(bsid)); pid = oBase::idFromExtId(oBase::converExtIdentifierString(bsid));
@ -2583,9 +2588,8 @@ pRunner IOF30Interface::readPerson(gdioutput &gdi, const xmlobject &person) {
if (s != sUnknown) if (s != sUnknown)
r->setSex(s); r->setSex(s);
person.getObjectString("BirthDate", tmp); person.getObjectString("BirthDate", tmp);
if (tmp.length()>=4) { if (tmp.length()>=4) {
tmp = tmp.substr(0, 4); r->setBirthDate(tmp);
r->setBirthYear(_wtoi(tmp.c_str()));
} }
getNationality(person.getObject("Nationality"), DI); getNationality(person.getObject("Nationality"), DI);
@ -2884,9 +2888,9 @@ void IOF30Interface::FeeInfo::add(IOF30Interface::FeeInfo &fi) {
if (!fi.toTime.empty()) { if (!fi.toTime.empty()) {
SYSTEMTIME st; SYSTEMTIME st;
convertDateYMS(fi.toTime, st, false); convertDateYMS(fi.toTime, st, false);
__int64 sec = SystemTimeToInt64Second(st); __int64 sec = SystemTimeToInt64TenthSecond(st);
sec -= 3600; sec -= timeConstHour;
fi.toTime = convertSystemDate(Int64SecondToSystemTime(sec)); fi.toTime = convertSystemDate(Int64TenthSecondToSystemTime(sec));
} }
} }
//if (fi.fromTime.empty() || (fi.fromTime < toTime && !toTime.empty())) //if (fi.fromTime.empty() || (fi.fromTime < toTime && !toTime.empty()))
@ -3069,7 +3073,7 @@ void IOF30Interface::setupRelayClass(pClass pc, const vector<LegInfo> &legs) {
pc->setNumStages(nStage); pc->setNumStages(nStage);
pc->setStartType(0, STTime, false); pc->setStartType(0, STTime, false);
pc->setStartData(0, oe.getAbsTime(3600)); pc->setStartData(0, oe.getAbsTime(timeConstHour));
int ix = 0; int ix = 0;
for (size_t k = 0; k < legs.size(); k++) { for (size_t k = 0; k < legs.size(); k++) {
@ -3097,10 +3101,24 @@ wstring IOF30Interface::getCurrentTime() const {
return getLocalDate() + L"T" + getLocalTimeOnly(); return getLocalDate() + L"T" + getLocalTimeOnly();
} }
wstring IOF30Interface::formatRelTime(int rt) {
wchar_t bf[32];
if (oe.useSubSecond())
swprintf_s(bf, L"%d.%d", rt / timeConstSecond, rt % timeConstSecond);
else
swprintf_s(bf, L"%d", rt / timeConstSecond);
return bf;
}
int IOF30Interface::parseISO8601Time(const xmlobject &xo) { int IOF30Interface::parseISO8601Time(const xmlobject &xo) {
if (!xo) if (!xo)
return 0; return 0;
const char *t = xo.getRaw(); const char *t = xo.getRawPtr();
if (!t)
return 0;
int tIx = -1; int tIx = -1;
int zIx = -1; int zIx = -1;
for (int k = 0; t[k] != 0; k++) { for (int k = 0; t[k] != 0; k++) {
@ -3160,9 +3178,9 @@ void IOF30Interface::getLocalDateTime(const string &date, const string &time,
int idate = convertDateYMS(date, st, true); int idate = convertDateYMS(date, st, true);
if (idate != -1) { if (idate != -1) {
if (zone == "Z" || zone == "z") { if (zone == "Z" || zone == "z") {
st.wHour = atime / 3600; st.wHour = atime / timeConstHour;
st.wMinute = (atime / 60) % 60; st.wMinute = (atime / timeConstMinute) % 60;
st.wSecond = atime % 60; st.wSecond = (atime / timeConstSecond) % 60;
SYSTEMTIME localTime; SYSTEMTIME localTime;
memset(&localTime, 0, sizeof(SYSTEMTIME)); memset(&localTime, 0, sizeof(SYSTEMTIME));
@ -3219,26 +3237,24 @@ void IOF30Interface::getLocalDateTime(const wstring &date, const wstring &time,
SYSTEMTIME st; SYSTEMTIME st;
memset(&st, 0, sizeof(SYSTEMTIME)); memset(&st, 0, sizeof(SYSTEMTIME));
int atime = convertAbsoluteTimeISO(wTime); const int atime = convertAbsoluteTimeISO(wTime);
int idate = convertDateYMS(date, st, true); int idate = convertDateYMS(date, st, true);
if (idate != -1) { if (idate != -1) {
if (zone == L"Z" || zone == L"z") { if (zone == L"Z" || zone == L"z") {
st.wHour = atime / 3600; st.wHour = atime / timeConstHour;
st.wMinute = (atime / 60) % 60; st.wMinute = (atime / timeConstMinute) % 60;
st.wSecond = atime % 60; st.wSecond = (atime / timeConstSecond) % 60;
SYSTEMTIME localTime; SYSTEMTIME localTime;
memset(&localTime, 0, sizeof(SYSTEMTIME)); memset(&localTime, 0, sizeof(SYSTEMTIME));
SystemTimeToTzSpecificLocalTime(0, &st, &localTime); SystemTimeToTzSpecificLocalTime(0, &st, &localTime);
atime = localTime.wHour * 3600 + localTime.wMinute * 60 + localTime.wSecond; //atime = localTime.wHour * 3600 + localTime.wMinute * 60 + localTime.wSecond;
wchar_t bf[64]; wchar_t bf[64];
wsprintf(bf, L"%02d:%02d:%02d", localTime.wHour, localTime.wMinute, localTime.wSecond); wsprintf(bf, L"%02d:%02d:%02d", localTime.wHour, localTime.wMinute, localTime.wSecond);
timeOut = bf; timeOut = bf;
wsprintf(bf, L"%d-%02d-%02d", localTime.wYear, localTime.wMonth, localTime.wDay); wsprintf(bf, L"%d-%02d-%02d", localTime.wYear, localTime.wMonth, localTime.wDay);
dateOut = bf; dateOut = bf;
//dateOut = itow(localTime.wYear) + L"-" + itow(localTime.wMonth) + L"-" + itow(localTime.wDay);
//timeOut = itow(localTime.wHour) + L":" + itow(localTime.wMinute) + L":" + itow(localTime.wSecond);
} }
else { else {
dateOut = date; dateOut = date;
@ -3460,16 +3476,16 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
xml.write("StartTime", oe.getAbsDateTimeISO(r.getStartTime(), true, useGMT)); xml.write("StartTime", oe.getAbsDateTimeISO(r.getStartTime(), true, useGMT));
bool hasTiming = (!r.getClassRef(false) || r.getClassRef(true)->getNoTiming() == false) && bool hasTiming = (!r.getClassRef(false) || r.getClassRef(true)->getNoTiming() == false) &&
r.getStatusComputed() != RunnerStatus::StatusNoTiming && !r.noTiming(); r.getStatusComputed(true) != RunnerStatus::StatusNoTiming && !r.noTiming();
int finishTime, runningTime, place, after; int finishTime, runningTime, place, after;
RunnerStatus status; RunnerStatus status;
if (!patrolResult) { if (!patrolResult) {
place = r.getPlace(); place = r.getPlace();
finishTime = r.getFinishTimeAdjusted(); finishTime = r.getFinishTimeAdjusted(false);
runningTime = r.getRunningTime(true); runningTime = r.getRunningTime(true);
after = r.getTimeAfter(); after = r.getTimeAfter();
status = r.getStatusComputed(); status = r.getStatusComputed(true);
} }
else { else {
int pl = r.getParResultLeg(); int pl = r.getParResultLeg();
@ -3480,7 +3496,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
else else
finishTime = 0; finishTime = 0;
after = r.getTeam()->getTimeAfter(pl); after = r.getTeam()->getTimeAfter(pl, true);
status = r.getTeam()->getLegStatus(pl, true, false); status = r.getTeam()->getLegStatus(pl, true, false);
} }
@ -3494,23 +3510,23 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
xml.write("FinishTime", oe.getAbsDateTimeISO(finishTime, true, useGMT)); xml.write("FinishTime", oe.getAbsDateTimeISO(finishTime, true, useGMT));
if (runningTime > 0) if (runningTime > 0)
xml.write("Time", runningTime); xml.write("Time", formatRelTime(runningTime));
if (after >= 0) { if (after >= 0) {
if (teamMember) { if (teamMember) {
xml.write("TimeBehind", "type", L"Leg", itow(after)); xml.write("TimeBehind", "type", L"Leg", formatRelTime(after));
int afterCourse = r.getTimeAfterCourse(); int afterCourse = r.getTimeAfterCourse();
if (afterCourse >= 0) if (afterCourse >= 0)
xml.write("TimeBehind", "type", L"Course", itow(afterCourse)); xml.write("TimeBehind", "type", L"Course", formatRelTime(afterCourse));
} }
else else
xml.write("TimeBehind", after); xml.write("TimeBehind", formatRelTime(after));
} }
if (r.getClassRef(false)) { if (r.getClassRef(false)) {
if (r.statusOK(true) && hasTiming) { if (r.statusOK(true, true) && hasTiming) {
if (!teamMember && place > 0 && place < 50000) { if (!teamMember && place > 0 && place < 50000) {
xml.write("Position", place); xml.write("Position", place);
} }
@ -3536,7 +3552,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
int rt = r.getTotalRunningTime(); int rt = r.getTotalRunningTime();
if (rt > 0 && hasTiming) if (rt > 0 && hasTiming)
xml.write("Time", rt); xml.write("Time", formatRelTime(rt));
RunnerStatus stat = r.getTotalStatus(); RunnerStatus stat = r.getTotalStatus();
@ -3546,7 +3562,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
int after = r.getTotalRunningTime() - int after = r.getTotalRunningTime() -
r.getClassRef(true)->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true); r.getClassRef(true)->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true);
if (after >= 0) if (after >= 0)
xml.write("TimeBehind", after); xml.write("TimeBehind", formatRelTime(after));
} }
if (stat == StatusOK && hasTiming) if (stat == StatusOK && hasTiming)
@ -3563,7 +3579,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
writeCourse(xml, *crs); writeCourse(xml, *crs);
const vector<SplitData> &sp = r.getSplitTimes(doUnroll); const vector<SplitData> &sp = r.getSplitTimes(doUnroll);
RunnerStatus st = r.getStatusComputed(); RunnerStatus st = r.getStatusComputed(true);
if (r.getStatus()>0 && st != StatusDNS && if (r.getStatus()>0 && st != StatusDNS &&
st != StatusCANCEL && st != StatusCANCEL &&
st != StatusNotCompetiting) { st != StatusNotCompetiting) {
@ -3579,7 +3595,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
break; break;
if (crs->getControl(k)->isRogaining(hasRogaining)) { if (crs->getControl(k)->isRogaining(hasRogaining)) {
if (sp[k].hasTime()) { if (sp[k].hasTime()) {
int time = sp[k].time - r.getStartTime(); int time = sp[k].getTime(false) - r.getStartTime();
int control = crs->getControl(k)->getFirstNumber(); int control = crs->getControl(k)->getFirstNumber();
rogaining.insert(make_pair(time, control)); rogaining.insert(make_pair(time, control));
} }
@ -3596,7 +3612,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
xml.startTag("SplitTime"); xml.startTag("SplitTime");
xml.write("ControlCode", crs->getControl(k)->getFirstNumber()); xml.write("ControlCode", crs->getControl(k)->getFirstNumber());
if (sp[k].hasTime() && hasTiming) if (sp[k].hasTime() && hasTiming)
xml.write("Time", sp[k].time - r.getStartTime()); xml.write("Time", formatRelTime(sp[k].getTime(false) - r.getStartTime()));
xml.endTag(); xml.endTag();
} }
@ -3604,7 +3620,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
xml.startTag("SplitTime", "status", "Additional"); xml.startTag("SplitTime", "status", "Additional");
xml.write("ControlCode", it->second); xml.write("ControlCode", it->second);
if (it->first != -1) if (it->first != -1)
xml.write("Time", it->first); xml.write("Time", formatRelTime(it->first));
xml.endTag(); xml.endTag();
} }
@ -3617,7 +3633,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o
xml.startTag("SplitTime", "status", "Additional"); xml.startTag("SplitTime", "status", "Additional");
xml.write("ControlCode", p->getTypeCode()); xml.write("ControlCode", p->getTypeCode());
if (p->getTimeInt() > r.getStartTime()) if (p->getTimeInt() > r.getStartTime())
xml.write("Time", p->getTimeInt() - r.getStartTime()); xml.write("Time", formatRelTime(p->getTimeInt() - r.getStartTime()));
xml.endTag(); xml.endTag();
} }
} }
@ -3653,7 +3669,8 @@ void IOF30Interface::writeFees(xmlparser &xml, const oRunner &r) const {
void IOF30Interface::writeTeamResult(xmlparser &xml, const oTeam &t, bool hasInputTime) { void IOF30Interface::writeTeamResult(xmlparser &xml, const oTeam &t, bool hasInputTime) {
xml.startTag("TeamResult"); xml.startTag("TeamResult");
xml.write("EntryId", t.getId()); writeTeamEntryId(t, xml);
xml.write("Name", t.getName()); xml.write("Name", t.getName());
if (t.getClubRef()) if (t.getClubRef())
@ -3672,6 +3689,24 @@ void IOF30Interface::writeTeamResult(xmlparser &xml, const oTeam &t, bool hasInp
xml.endTag(); xml.endTag();
} }
void IOF30Interface::writeTeamEntryId(const oTeam& t, xmlparser& xml)
{
bool isImported = t.getEntrySource() != 0;
wstring id;
if (t.getExtIdentifier() != 0) {
id = t.getExtIdentifierString();
isImported = true;
}
else
id = itow(t.getId());
if (isImported)
xml.write("EntryId", id);
else if (!useEventorQuirks) {
xml.write("EntryId", "type", L"MeOS", id);
}
}
int IOF30Interface::getStageNumber() { int IOF30Interface::getStageNumber() {
if (cachedStageNumber >= 0) if (cachedStageNumber >= 0)
return cachedStageNumber; return cachedStageNumber;
@ -4002,7 +4037,8 @@ void IOF30Interface::writeTeamNoPersonStart(xmlparser &xml, const oTeam &t, int
void IOF30Interface::writeTeamStart(xmlparser &xml, const oTeam &t) { void IOF30Interface::writeTeamStart(xmlparser &xml, const oTeam &t) {
xml.startTag("TeamStart"); xml.startTag("TeamStart");
xml.write("EntryId", t.getId()); writeTeamEntryId(t, xml);
xml.write("Name", t.getName()); xml.write("Name", t.getName());
if (t.getClubRef()) if (t.getClubRef())
@ -4072,7 +4108,25 @@ void IOF30Interface::writeLegOrder(xmlparser &xml, const oClass *pc, int legNo)
} }
} }
bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) { size_t hash_entry(const wstring& name, int clubId, int card, const wstring &birth) {
size_t h = 0;
for (int j = 0; j < name.size(); j++) {
h = h * 37 + name[j];
}
h = h * 997 + clubId;
h = h * 997 + card;
for (int j = 0; j < birth.size(); j++) {
if (birth[j] >= '0' && birth[j] <= '9')
h = h * 997 + birth[j];
}
return h;
}
bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor,
bool onlyWithClub,
unordered_multimap<size_t, int>& duplicateCheck,
int &duplicateCount) {
if (!xCompetitor) return false; if (!xCompetitor) return false;
@ -4083,10 +4137,7 @@ bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) {
int pidI; int pidI;
long long pid; long long pid;
readId(person, pidI, pid); readId(person, pidI, pid);
/*
wstring pidS;
person.getObjectString("Id", pidS);xxx
long long pid = oBase::converExtIdentifierString(pidS);*/
xmlobject pname = person.getObject("Name"); xmlobject pname = person.getObject("Name");
if (!pname) return false; if (!pname) return false;
@ -4100,7 +4151,7 @@ bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) {
xmlobject &card = cards[k]; xmlobject &card = cards[k];
if (card) { if (card) {
xmlattrib pSystem = card.getAttrib("punchingSystem"); xmlattrib pSystem = card.getAttrib("punchingSystem");
if (!pSystem || _stricmp(pSystem.get(), "SI") == 0) { if (!pSystem || _stricmp(pSystem.getPtr(), "SI") == 0) {
cardno = card.getObjectInt(0); cardno = card.getObjectInt(0);
break; break;
} }
@ -4116,13 +4167,13 @@ bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) {
if (given.empty() || family.empty()) if (given.empty() || family.empty())
return false; return false;
//string name(given+" "+family);
wstring name(family + L", " + given); wstring name(family + L", " + given);
char sex[2]; char sex[2];
person.getObjectString("sex", sex, 2); person.getObjectString("sex", sex, 2);
int birth = person.getObjectInt("BirthDate"); wstring birth;
person.getObjectString("BirthDate", birth);
xmlobject nat=person.getObject("Nationality"); xmlobject nat=person.getObject("Nationality");
@ -4137,8 +4188,26 @@ bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) {
clubId = xClub.getObjectInt("Id"); clubId = xClub.getObjectInt("Id");
} }
if (onlyWithClub && clubId == 0)
return false;
size_t eHash = hash_entry(name, clubId, cardno, birth);
RunnerDB &runnerDB = oe.getRunnerDatabase(); RunnerDB &runnerDB = oe.getRunnerDatabase();
auto dupCheck = duplicateCheck.equal_range(eHash);
wstring dupName;
for (auto it = dupCheck.first; it != dupCheck.second; ++it) {
int ix = it->second;
RunnerWDBEntry* dupCand = runnerDB.getRunnerByIndex(ix);
dupCand->getName(dupName);
if (dupName == name && clubId == dupCand->dbe().clubNo && cardno == dupCand->dbe().cardNo) {
duplicateCount++;
return false; // Duplicate person
}
}
RunnerWDBEntry *rde = runnerDB.getRunnerById(pid); RunnerWDBEntry *rde = runnerDB.getRunnerById(pid);
if (!rde) { if (!rde) {
@ -4152,10 +4221,12 @@ bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) {
} }
if (rde) { if (rde) {
duplicateCheck.emplace(eHash, rde->getIndex());
rde->setExtId(pid); rde->setExtId(pid);
rde->setName(name.c_str()); rde->setName(name.c_str());
rde->dbe().cardNo = cardno;
rde->dbe().clubNo = clubId; rde->dbe().clubNo = clubId;
rde->dbe().birthYear = extendYear(birth); rde->dbe().setBirthDate(birth);
rde->dbe().sex = sex[0]; rde->dbe().sex = sex[0];
memcpy(rde->dbe().national, national, 3); memcpy(rde->dbe().national, national, 3);
} }
@ -4276,14 +4347,14 @@ bool IOF30Interface::readControl(const xmlobject &xControl) {
pControl pc = 0; pControl pc = 0;
if (type == 0) { if (type == 0) {
pc = oe.getControl(code, true); pc = oe.getControl(code, true, false);
} }
else if (type == 1) { else if (type == 1) {
wstring start = getStartName(trim(idStr)); wstring start = getStartName(trim(idStr));
pc = oe.getControl(getStartIndex(idStr), true); pc = oe.getControl(getStartIndex(idStr), true, false);
pc->setNumbers(L""); pc->setNumbers(L"");
pc->setName(start); pc->setName(start);
pc->setStatus(oControl::StatusStart); pc->setStatus(oControl::ControlStatus::StatusStart);
} }
else if (type == 2) { else if (type == 2) {
wstring finish = trim(idStr); wstring finish = trim(idStr);
@ -4294,10 +4365,10 @@ bool IOF30Interface::readControl(const xmlobject &xControl) {
finish = lang.tl(L"Mål ") + itow(num); finish = lang.tl(L"Mål ") + itow(num);
else else
finish = lang.tl(L"Mål"); finish = lang.tl(L"Mål");
pc = oe.getControl(getFinishIndex(num), true); pc = oe.getControl(getFinishIndex(num), true, false);
pc->setNumbers(L""); pc->setNumbers(L"");
pc->setName(finish); pc->setName(finish);
pc->setStatus(oControl::StatusFinish); pc->setStatus(oControl::ControlStatus::StatusFinish);
} }
if (pc) { if (pc) {
@ -4395,7 +4466,7 @@ pCourse IOF30Interface::readCourse(const xmlobject &xcrs) {
if (type == "Start" && startName.empty()) { if (type == "Start" && startName.empty()) {
wstring idStr; wstring idStr;
xControls[k].getObjectString("Control", idStr); xControls[k].getObjectString("Control", idStr);
pControl pStart = oe.getControl(getStartIndex(idStr), false); pControl pStart = oe.getControl(getStartIndex(idStr), false, false);
if (pStart) if (pStart)
startName = pStart->getName(); startName = pStart->getName();
} }
@ -4407,14 +4478,14 @@ pCourse IOF30Interface::readCourse(const xmlobject &xcrs) {
xControls[k].getObjects("Control", xPunchControls); xControls[k].getObjects("Control", xPunchControls);
pControl pCtrl = 0; pControl pCtrl = 0;
if (xPunchControls.size() == 1) { if (xPunchControls.size() == 1) {
pCtrl = oe.getControl(xPunchControls[0].getInt(), true); pCtrl = oe.getControl(xPunchControls[0].getInt(), true, false);
} }
else if (xPunchControls.size()>1) { else if (xPunchControls.size()>1) {
pCtrl = oe.addControl(1000*cid + xPunchControls[0].getInt(),xPunchControls[0].getInt(), L""); pCtrl = oe.addControl(1000*cid + xPunchControls[0].getInt(),xPunchControls[0].getInt(), L"");
if (pCtrl) { if (pCtrl) {
wstring cc; wstring cc;
for (size_t j = 0; j < xPunchControls.size(); j++) for (size_t j = 0; j < xPunchControls.size(); j++)
cc += wstring(xPunchControls[j].getw()) + L" "; cc += xPunchControls[j].getWStr() + L" ";
pCtrl->setNumbers(cc); pCtrl->setNumbers(cc);
} }
@ -4426,7 +4497,7 @@ pCourse IOF30Interface::readCourse(const xmlobject &xcrs) {
int score = xControls[k].getObjectInt("Score"); int score = xControls[k].getObjectInt("Score");
if (score > 0) { if (score > 0) {
pCtrl->getDI().setInt("Rogaining", score); pCtrl->getDI().setInt("Rogaining", score);
pCtrl->setStatus(oControl::StatusRogaining); pCtrl->setStatus(oControl::ControlStatus::StatusRogaining);
hasRogaining = true; hasRogaining = true;
} }
} }
@ -4459,7 +4530,7 @@ pCourse IOF30Interface::readCourse(const xmlobject &xcrs) {
if (hasRogaining) { if (hasRogaining) {
int mt = oe.getMaximalTime(); int mt = oe.getMaximalTime();
if (mt == 0) if (mt == 0)
mt = 3600; mt = timeConstHour;
pc->setMaximumRogainingTime(mt); pc->setMaximumRogainingTime(mt);
} }
@ -4508,7 +4579,7 @@ void IOF30Interface::writeCourses(xmlparser &xml) {
xml.endTag(); xml.endTag();
set<wstring> ids; set<wstring> ids;
for (size_t k = 0; k < ctrl.size(); k++) { for (size_t k = 0; k < ctrl.size(); k++) {
if (ctrl[k]->getStatus() != oControl::StatusFinish && ctrl[k]->getStatus() != oControl::StatusStart) { if (!oControl::isSpecialControl(ctrl[k]->getStatus())) {
wstring id = writeControl(xml, *ctrl[k], ids); wstring id = writeControl(xml, *ctrl[k], ids);
ctrlId2ExportId[ctrl[k]->getId()] = id; ctrlId2ExportId[ctrl[k]->getId()] = id;
} }

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -25,6 +25,7 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include <unordered_map>
class oEvent; class oEvent;
class xmlobject; class xmlobject;
@ -72,7 +73,8 @@ class IOF30Interface {
bool unrollLoops; bool unrollLoops;
// Include data on stage number // Include data on stage number
bool includeStageRaceInfo; bool includeStageRaceInfo;
void operator=(const IOF30Interface &);
const IOF30Interface &operator=(const IOF30Interface &) = delete;
set<wstring> matchedClasses; set<wstring> matchedClasses;
@ -193,6 +195,7 @@ class IOF30Interface {
int parseISO8601Time(const xmlobject &xo); int parseISO8601Time(const xmlobject &xo);
wstring getCurrentTime() const; wstring getCurrentTime() const;
wstring formatRelTime(int rt);
static void getNationality(const xmlobject &xCountry, oDataInterface &di); static void getNationality(const xmlobject &xCountry, oDataInterface &di);
@ -220,6 +223,8 @@ class IOF30Interface {
void writeTeamResult(xmlparser &xml, const oTeam &t, bool hasInputTime); void writeTeamResult(xmlparser &xml, const oTeam &t, bool hasInputTime);
void writeTeamEntryId(const oTeam& t, xmlparser& xml);
void writeResult(xmlparser &xml, const oRunner &rPerson, const oRunner &rResultCarrier, void writeResult(xmlparser &xml, const oRunner &rPerson, const oRunner &rResultCarrier,
bool includeCourse, bool includeRaceNumber, bool teamMember, bool hasInputTime); bool includeCourse, bool includeRaceNumber, bool teamMember, bool hasInputTime);
@ -249,7 +254,10 @@ class IOF30Interface {
// Returns zero if no stage number // Returns zero if no stage number
int getStageNumber(); int getStageNumber();
bool readXMLCompetitorDB(const xmlobject &xCompetitor); bool readXMLCompetitorDB(const xmlobject &xCompetitor,
bool onlyWithClub,
unordered_multimap<size_t, int> &duplicateCheck,
int &duplicateCount);
void writeXMLCompetitorDB(xmlparser &xml, const RunnerDB &db, const RunnerWDBEntry &rde) const; void writeXMLCompetitorDB(xmlparser &xml, const RunnerDB &db, const RunnerWDBEntry &rde) const;
int getStartIndex(const wstring &startId); int getStartIndex(const wstring &startId);
@ -258,7 +266,7 @@ class IOF30Interface {
pCourse readCourse(const xmlobject &xcrs); pCourse readCourse(const xmlobject &xcrs);
void readCourseGroups(xmlobject xClassCourse, vector< vector<pCourse> > &crs); void readCourseGroups(xmlobject xClassCourse, vector< vector<pCourse> > &crs);
void bindClassCourse(oClass &pc, const vector< vector<pCourse> > &crs); void bindClassCourse(oClass &pc, const vector<vector<pCourse>> &crs);
static wstring constructCourseName(const xmlobject &xcrs); static wstring constructCourseName(const xmlobject &xcrs);
static wstring constructCourseName(const wstring &family, const wstring &name); static wstring constructCourseName(const wstring &family, const wstring &name);
@ -290,8 +298,10 @@ class IOF30Interface {
set<int> readCrsIds; set<int> readCrsIds;
bool useEventorQuirks;
public: public:
IOF30Interface(oEvent *oe, bool forceSplitFee); IOF30Interface(oEvent *oe, bool forceSplitFee, bool useEventorQuirks);
virtual ~IOF30Interface() {} virtual ~IOF30Interface() {}
static void getLocalDateTime(const wstring &datetime, wstring &dateOut, wstring &timeOut); static void getLocalDateTime(const wstring &datetime, wstring &dateOut, wstring &timeOut);
@ -321,7 +331,8 @@ public:
void readClassList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail); void readClassList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail);
void prescanCompetitorList(xmlobject &xo); void prescanCompetitorList(xmlobject &xo);
void readCompetitorList(gdioutput &gdi, const xmlobject &xo, int &personCount); void readCompetitorList(gdioutput &gdi, const xmlobject &xo,
bool onlyWithClub, int &personCount, int& duplicateCount);
void readClubList(gdioutput &gdi, const xmlobject &xo, int &clubCount); void readClubList(gdioutput &gdi, const xmlobject &xo, int &clubCount);

View File

@ -7,7 +7,7 @@ Third Party Code. Additional copyright notices and license terms applicable to p
All trademarks and registered trademarks mentioned herein are the property of their respective owners. All trademarks and registered trademarks mentioned herein are the property of their respective owners.
------------------------------------ ------------------------------------
Copyright 2007-2020 Melin Software HB. Copyright 2007-2023 Melin Software HB.
------------------------------------ ------------------------------------

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -37,6 +37,9 @@
#include "generalresult.h" #include "generalresult.h"
#include "gdiconstants.h" #include "gdiconstants.h"
#include "autocomplete.h" #include "autocomplete.h"
#include "image.h"
extern Image image;
ListEditor::ListEditor(oEvent *oe_) { ListEditor::ListEditor(oEvent *oe_) {
oe = oe_; oe = oe_;
@ -62,13 +65,6 @@ void ListEditor::setCurrentList(MetaList *lst) {
delete currentList; delete currentList;
currentList = lst; currentList = lst;
} }
/*
void ListEditor::load(MetaList *list) {
currentList = list;
currentIndex = -1;
dirtyInt = true;
dirtyExt = true;
}*/
void ListEditor::load(const MetaListContainer &mlc, int index) { void ListEditor::load(const MetaListContainer &mlc, int index) {
const MetaList &mc = mlc.getList(index); const MetaList &mc = mlc.getList(index);
@ -117,6 +113,8 @@ void ListEditor::show(gdioutput &gdi) {
gdi.fillRight(); gdi.fillRight();
gdi.addButton("EditList", "Egenskaper", editListCB); gdi.addButton("EditList", "Egenskaper", editListCB);
gdi.addButton("SplitPrint", "Sträcktidslista", editListCB);
gdi.setCX(gdi.getCX() + gdi.scaleLength(32)); gdi.setCX(gdi.getCX() + gdi.scaleLength(32));
gdi.addButton("OpenFile", "Öppna fil", editListCB); gdi.addButton("OpenFile", "Öppna fil", editListCB);
gdi.addButton("OpenInside", "Öppna från aktuell tävling", editListCB); gdi.addButton("OpenInside", "Öppna från aktuell tävling", editListCB);
@ -137,6 +135,21 @@ void ListEditor::show(gdioutput &gdi) {
gdi.dropLine(2); gdi.dropLine(2);
int dx = gdi.getCX(); int dx = gdi.getCX();
if (currentList && currentList->isSplitPrintList()) {
gdi.setCX(bx);
gdi.addString("", 0, "Välj deltagare för förhandsgranskning:");
gdi.addSelection("Runner", 300, 300, editListCB);
oe->fillRunners(gdi, "Runner", false, 0);
if (currentRunnerId > 0 && oe->getRunner(currentRunnerId, 0))
gdi.selectItemByData("Runner", currentRunnerId);
else {
gdi.selectFirstItem("Runner");
currentRunnerId = gdi.getSelectedItem("Runner").first;
}
gdi.dropLine(2);
}
int dy = gdi.getCY(); int dy = gdi.getCY();
RECT rc; RECT rc;
@ -156,6 +169,7 @@ void ListEditor::show(gdioutput &gdi) {
makeDirty(gdi, NoTouch, NoTouch); makeDirty(gdi, NoTouch, NoTouch);
if (!currentList) { if (!currentList) {
gdi.disableInput("EditList"); gdi.disableInput("EditList");
gdi.disableInput("SplitPrint");
gdi.disableInput("SaveFile"); gdi.disableInput("SaveFile");
gdi.disableInput("SaveFileCopy", true); gdi.disableInput("SaveFileCopy", true);
gdi.disableInput("SaveInside"); gdi.disableInput("SaveInside");
@ -223,8 +237,17 @@ void ListEditor::show(gdioutput &gdi) {
gdi.addButton("AddLine3", "Lägg till rad", editListCB); gdi.addButton("AddLine3", "Lägg till rad", editListCB);
gdi.setRestorePoint("EditList"); gdi.setRestorePoint("EditList");
renderListPreview(gdi);
gdi.refresh();
}
void ListEditor::renderListPreview(gdioutput &gdi) {
gdi.dropLine(2); gdi.dropLine(2);
gdi.fillDown();
RECT rc;
pRunner splitPrintR = nullptr;
if (currentList->isSplitPrintList() && currentRunnerId)
splitPrintR = oe->getRunner(currentRunnerId, 0);
oListInfo li; oListInfo li;
oListParam par; oListParam par;
@ -232,30 +255,59 @@ void ListEditor::show(gdioutput &gdi) {
par.splitAnalysis = true; par.splitAnalysis = true;
par.setLegNumberCoded(-1); par.setLegNumberCoded(-1);
par.inputNumber = 0; par.inputNumber = 0;
gdi.fillDown();
if (splitPrintR) {
par.selection.insert(splitPrintR->getClassId(true));
par.showInterTimes = false;
par.setLegNumberCoded(splitPrintR->getLegNumber());
par.filterMaxPer = 3;
par.alwaysInclude = splitPrintR;
par.showHeader = false;
}
double originalScale = 0;
try { try {
currentList->interpret(oe, gdi, par, li);
rc.left = gdi.getCX(); rc.left = gdi.getCX();
rc.right = gdi.getCX() + gdi.getWidth() - 20; rc.right = gdi.getCX() + gdi.getWidth() - gdi.scaleLength(20);
rc.top = gdi.getCY(); rc.top = gdi.getCY();
rc.bottom = rc.top + 4; rc.bottom = rc.top + 4;
gdi.addRectangle(rc, colorDarkGreen, false, false); gdi.addRectangle(rc, colorDarkGreen, false, false);
gdi.dropLine(); gdi.dropLine();
oe->generateList(gdi, false, li, true); currentList->interpret(oe, gdi, par, li);
if (splitPrintR) {
auto& sp = *li.getSplitPrintInfo();
li.getParam().filterMaxPer = sp.numClassResults;
const bool wideFormat = oe->getPropertyInt("WideSplitFormat", 0) == 1;
if (!wideFormat)
li.shrinkSize();
rc.left = gdi.getCX();
rc.top = gdi.getCY();
gdi.setCX(gdi.getCX() + gdi.scaleLength(10));
gdi.dropLine();
splitPrintR->printSplits(gdi, &li);
gdi.dropLine();
rc.right = rc.left + gdi.scaleLength(250);
rc.bottom = gdi.getHeight();
gdi.addRectangle(rc, GDICOLOR::colorLightYellow);
gdi.refresh();
}
else {
oe->generateList(gdi, false, li, true);
}
} }
catch (meosException &ex) { catch (meosException& ex) {
gdi.addString("", 1, "Listan kan inte visas").setColor(colorRed); gdi.addString("", 1, "Listan kan inte visas").setColor(colorRed);
gdi.addString("", 0, ex.wwhat()); gdi.addString("", 0, ex.wwhat());
} }
catch (std::exception &ex) { catch (std::exception& ex) {
gdi.addString("", 1, "Listan kan inte visas").setColor(colorRed); gdi.addString("", 1, "Listan kan inte visas").setColor(colorRed);
gdi.addString("", 0, ex.what()); gdi.addString("", 0, ex.what());
} }
gdi.refresh();
} }
int editListCB(gdioutput *gdi, int type, void *data) int editListCB(gdioutput *gdi, int type, void *data)
@ -274,7 +326,8 @@ void ListEditor::showLine(gdioutput &gdi, const vector<MetaListPost> &line, int
addButton(gdi, line[k], gdi.getCX(), gdi.getCY(), ix, k); addButton(gdi, line[k], gdi.getCX(), gdi.getCY(), ix, k);
} }
gdi.addButton("AddPost" + itos(ix), "Lägg till ny", editListCB); gdi.addButton("AddPost", "Lägg till ny", editListCB).setExtra(ix);
gdi.addButton("AddImage", "Lägg till bild", editListCB).setExtra(ix);
} }
ButtonInfo &ListEditor::addButton(gdioutput &gdi, const MetaListPost &mlp, int x, int y, int lineIx, int ix) const { ButtonInfo &ListEditor::addButton(gdioutput &gdi, const MetaListPost &mlp, int x, int y, int lineIx, int ix) const {
@ -282,6 +335,17 @@ ButtonInfo &ListEditor::addButton(gdioutput &gdi, const MetaListPost &mlp, int x
if (mlp.getType() == L"String") { if (mlp.getType() == L"String") {
cap = L"Text: X#" + mlp.getText(); cap = L"Text: X#" + mlp.getText();
} }
else if (mlp.getType() == L"Image") {
if (mlp.getText().empty())
cap = L"Image";
else {
uint64_t imgId = mlp.getImageId();
if (!image.hasImage(imgId))
cap = L"Error";
else
cap = image.getFileName(imgId);
}
}
else { else {
const wstring &text = mlp.getText(); const wstring &text = mlp.getText();
if (text.length() > 0) { if (text.length() > 0) {
@ -364,6 +428,26 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
if (ii.id == "Text" && ii.text != lastShownExampleText) { if (ii.id == "Text" && ii.text != lastShownExampleText) {
showExample(gdi); showExample(gdi);
} }
else if (ii.id == "ImgWidth" || ii.id == "ImgHeight") {
if (gdi.isChecked("PreserveAspectRatio")) {
auto sel = gdi.getSelectedItem("Image");
if (sel.second && sel.first >= 0) {
auto imgId = image.getIdFromEnumeration(sel.first);
int h = image.getHeight(imgId);
int w = image.getWidth(imgId);
int ww, hh;
if (ii.id == "ImgWidth") {
ww = _wtoi(ii.text.c_str());
hh = (ww * h + w/2) / w;
gdi.setText("ImgHeight", hh);
} else {
hh = _wtoi(ii.text.c_str());
ww = (hh * w + h / 2) / h;
gdi.setText("ImgWidth", ww);
}
}
}
}
} }
else if (type == GUI_BUTTON) { else if (type == GUI_BUTTON) {
ButtonInfo bi = dynamic_cast<ButtonInfo &>(data); ButtonInfo bi = dynamic_cast<ButtonInfo &>(data);
@ -376,66 +460,64 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
biSrc.setExtra(res); biSrc.setExtra(res);
oe->setProperty("Colors", c); oe->setProperty("Colors", c);
} }
/*CHOOSECOLOR cc;
memset(&cc, 0, sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = gdi.getHWND();
cc.rgbResult = COLORREF(bi.getExtra());
if (GDICOLOR((int)bi.getExtra()) != colorDefault)
cc.Flags |= CC_RGBINIT;
COLORREF staticColor[16];
memset(staticColor, 0, 16*sizeof(COLORREF));
const string &c = oe->getPropertyString("Colors", "");
const char *end = c.c_str() + c.length();
const char * pEnd = c.c_str();
int pix = 0;
while(pEnd < end && pix < 16) {
staticColor[pix++] = strtol(pEnd,(char **)&pEnd,16);
}
cc.lpCustColors = staticColor;
if (ChooseColor(&cc)) {
data.setExtra((int)cc.rgbResult);
wstring co;
for (ix = 0; ix < 16; ix++) {
wchar_t bf[16];
swprintf_s(bf, L"%x ", staticColor[ix]);
co += bf;
}
oe->setProperty("Colors", co);
}*/
} }
if ( bi.id.substr(0, 8) == "EditPost" ) { else if (bi.id == "NewImage") {
vector<pair<wstring, wstring>> ext = { make_pair(L">Bilder", L"*.png") };
wstring fn = gdi.browseForOpen(ext, L"png");
if (!fn.empty()) {
bool transparent = gdi.isChecked("TransparentWhite");
uint64_t imgId = image.loadFromFile(fn, transparent ? Image::ImageMethod::WhiteTransparent : Image::ImageMethod::Default);
int selIx = selectImage(gdi, imgId);
previewImage(gdi, selIx);
updateImageStatus(gdi, selIx);
}
}
else if (bi.id == "TransparentWhite") {
bool transparent = gdi.isChecked("TransparentWhite");
int data = gdi.getSelectedItem("Image").first;
if (data >= 0) {
uint64_t imgId = image.getIdFromEnumeration(data);
image.reloadImage(imgId, transparent ? Image::ImageMethod::WhiteTransparent : Image::ImageMethod::Default);
gdi.refresh();
}
}
else if ( bi.id.substr(0, 8) == "EditPost" ) {
if (gdi.hasData("CurrentId")) { if (gdi.hasData("CurrentId")) {
DWORD id; checkUnsaved(gdi);
gdi.getData("CurrentId", id);
getPosFromId(id, groupIx, lineIx, ix);
MetaListPost &mlp = currentList->getMLP(groupIx, lineIx, ix);
saveListPost(gdi, mlp);
} }
int id = atoi(bi.id.substr(8).c_str()); int id = atoi(bi.id.substr(8).c_str());
getPosFromId(id, groupIx, lineIx, ix); getPosFromId(id, groupIx, lineIx, ix);
MetaListPost &mlp = currentList->getMLP(groupIx, lineIx, ix); MetaListPost &mlp = currentList->getMLP(groupIx, lineIx, ix);
editListPost(gdi, mlp, id); if (mlp.getTypeRaw() == EPostType::lImage)
editImage(gdi, mlp, id);
else
editListPost(gdi, mlp, id);
} }
else if ( bi.id.substr(0, 7) == "AddPost" ) { else if (bi.id == "AddPost" || bi.id == "AddImage") {
checkUnsaved(gdi); checkUnsaved(gdi);
gdi.restore("EditList", true); gdi.restore("EditList", true);
gdi.pushX(); gdi.pushX();
lineIx = atoi(bi.id.substr(7).c_str()); lineIx = bi.getExtraInt();
groupIx = (lineIx / 100) - 1; groupIx = (lineIx / 100) - 1;
int ixOutput = 0; int ixOutput = 0;
MetaListPost &mlp = currentList->addNew(groupIx, lineIx % 100, ixOutput); MetaListPost &mlp = currentList->addNew(groupIx, lineIx % 100, ixOutput);
int xp = bi.xp; if (bi.id == "AddImage")
int yp = bi.yp; mlp.setType(EPostType::lImage);
auto& post = dynamic_cast<ButtonInfo&>(gdi.getBaseInfo("AddPost", lineIx));
int xp = post.xp;
int yp = post.yp;
ButtonInfo &nb = addButton(gdi, mlp, xp, yp, lineIx, ixOutput); ButtonInfo &nb = addButton(gdi, mlp, xp, yp, lineIx, ixOutput);
//gdi.addButton(xp, yp, string("Foo"), string("FoooBar"), 0);
int w, h; int w, h;
nb.getDimension(gdi, w, h); nb.getDimension(gdi, w, h);
biSrc.moveButton(gdi, xp+w, yp); post.moveButton(gdi, xp + w, yp);
int w2, h2;
post.getDimension(gdi, w2, h2);
dynamic_cast<ButtonInfo&>(gdi.getBaseInfo("AddImage", lineIx)).moveButton(gdi, xp + w + w2, yp);
gdi.popX(); gdi.popX();
gdi.setRestorePoint("EditList"); gdi.setRestorePoint("EditList");
makeDirty(gdi, MakeDirty, MakeDirty); makeDirty(gdi, MakeDirty, MakeDirty);
@ -463,9 +545,13 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
else if (bi.id == "UseLeg") { else if (bi.id == "UseLeg") {
gdi.setInputStatus("Leg", gdi.isChecked(bi.id)); gdi.setInputStatus("Leg", gdi.isChecked(bi.id));
} }
else if (bi.id == "UseForSplit") {
statusSplitPrint(gdi, gdi.isChecked(bi.id));
}
else if (bi.id == "Cancel") { else if (bi.id == "Cancel") {
gdi.restore("EditList"); gdi.restore("EditList");
gdi.enableInput("EditList"); gdi.enableInput("EditList");
gdi.enableInput("SplitPrint");
} }
else if (bi.id == "CancelNew") { else if (bi.id == "CancelNew") {
gdi.clearPage(false); gdi.clearPage(false);
@ -473,26 +559,35 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
show(gdi); show(gdi);
} }
else if (bi.id == "Apply" || bi.id == "MoveLeft" || bi.id == "MoveRight") { else if (bi.id == "Apply" || bi.id == "MoveLeft" || bi.id == "MoveRight") {
bool image = gdi.hasData("IsEditingImage");
DWORD id; DWORD id;
gdi.getData("CurrentId", id); gdi.getData("CurrentId", id);
getPosFromId(id, groupIx, lineIx, ix); getPosFromId(id, groupIx, lineIx, ix);
if (bi.id == "MoveLeft") if (bi.id == "MoveLeft") {
currentList->moveOnRow(groupIx, lineIx, ix, -1); currentList->moveOnRow(groupIx, lineIx, ix, -1);
else if (bi.id == "MoveRight") id--;
}
else if (bi.id == "MoveRight") {
currentList->moveOnRow(groupIx, lineIx, ix, 1); currentList->moveOnRow(groupIx, lineIx, ix, 1);
id++;
}
gdi.setData("CurrentId", id);
MetaListPost &mlp = currentList->getMLP(groupIx, lineIx, ix); MetaListPost &mlp = currentList->getMLP(groupIx, lineIx, ix);
bool force = saveListPost(gdi, mlp); bool force = checkUnsaved(gdi);//saveListPost(gdi, mlp);
if (!gdi.hasData("NoRedraw") || force) { if (!gdi.hasData("NoRedraw") || force) {
gdi.restore("BeginListEdit", false); gdi.restore("BeginListEdit", false);
show(gdi); show(gdi);
} }
if (bi.id != "Apply") if (bi.id != "Apply") {
editListPost(gdi, mlp, bi.getExtraInt()); if (image)
editImage(gdi, mlp, bi.getExtraInt());
else
editListPost(gdi, mlp, bi.getExtraInt());
}
} }
else if (bi.id == "ApplyListProp") { else if (bi.id == "ApplyListProp") {
wstring name = gdi.getText("Name"); wstring name = gdi.getText("Name");
@ -532,7 +627,6 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
list.setSubFilters(subFiltersOut); list.setSubFilters(subFiltersOut);
for (int k = 0; k < 4; k++) { for (int k = 0; k < 4; k++) {
list.setFontFace(k, gdi.getText("Font" + itos(k)), list.setFontFace(k, gdi.getText("Font" + itos(k)),
gdi.getTextNo("FontFactor" + itos(k))); gdi.getTextNo("FontFactor" + itos(k)));
@ -541,7 +635,6 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
list.setExtraSpace(k, f); list.setExtraSpace(k, f);
} }
list.setSupportFromTo(gdi.isChecked("SupportFrom"), gdi.isChecked("SupportTo")); list.setSupportFromTo(gdi.isChecked("SupportFrom"), gdi.isChecked("SupportTo"));
list.setSupportLegSelection(gdi.isChecked("SupportLegSelection")); list.setSupportLegSelection(gdi.isChecked("SupportLegSelection"));
@ -552,9 +645,32 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
show(gdi); show(gdi);
} }
} }
else if (bi.id == "ApplySplitList") {
MetaList& list = *currentList;
if (!gdi.isChecked("UseForSplit"))
list.setSplitPrintInfo(nullptr);
else {
auto spInfo = make_shared<SplitPrintListInfo>();
spInfo->includeSplitTimes = gdi.isChecked("Split");
spInfo->withSpeed = gdi.isChecked("Speed");
spInfo->withResult = gdi.isChecked("Result");
spInfo->withAnalysis = gdi.isChecked("Analysis");
list.setSplitPrintInfo(spInfo);
}
makeDirty(gdi, MakeDirty, MakeDirty);
if (!gdi.hasData("NoRedraw")) {
gdi.clearPage(false);
show(gdi);
}
}
else if (bi.id == "EditList") { else if (bi.id == "EditList") {
editListProp(gdi, false); editListProp(gdi, false);
} }
else if (bi.id == "SplitPrint") {
splitPrintList(gdi);
}
else if (bi.id == "NewList") { else if (bi.id == "NewList") {
if (!checkSave(gdi)) if (!checkSave(gdi))
return 0; return 0;
@ -645,7 +761,7 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
gdi.pushX(); gdi.pushX();
vector< pair<wstring, size_t> > lists; vector< pair<wstring, size_t> > lists;
oe->getListContainer().getLists(lists, true, false, false); oe->getListContainer().getLists(lists, true, false, false, false);
gdi.fillRight(); gdi.fillRight();
gdi.addSelection("OpenList", 250, 400, editListCB, L"Välj lista:"); gdi.addSelection("OpenList", 250, 400, editListCB, L"Välj lista:");
@ -691,6 +807,11 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
savedFileName.clear(); savedFileName.clear();
oe->synchronize(false); oe->synchronize(false);
set<uint64_t> imgUsed;
currentList->getUsedImages(imgUsed);
for (uint64_t id : imgUsed)
oe->saveImage(id);
if (currentIndex != -1) { if (currentIndex != -1) {
oe->getListContainer().saveList(currentIndex, *currentList); oe->getListContainer().saveList(currentIndex, *currentList);
} }
@ -733,15 +854,6 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
} }
return 0; return 0;
} }
/*else if (bi.id == "BrowseFont") {
InitCommonControls();
CHOOSEFONT cf;
memset(&cf, 0, sizeof(cf));
cf.lStructSize = sizeof(cf);
cf.hwndOwner = gdi.getHWND();
ChooseFont(&cf);
EnumFontFamilies(
}*/
} }
else if (type == GUI_LISTBOX) { else if (type == GUI_LISTBOX) {
ListBoxInfo &lbi = dynamic_cast<ListBoxInfo &>(data); ListBoxInfo &lbi = dynamic_cast<ListBoxInfo &>(data);
@ -758,6 +870,10 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
else else
gdi.setText("AlignText", L""); gdi.setText("AlignText", L"");
} }
else if (lbi.id == "Image") {
previewImage(gdi, lbi.data);
updateImageStatus(gdi, lbi.data);
}
else if (lbi.id == "Type") { else if (lbi.id == "Type") {
updateType(lbi.data, gdi); updateType(lbi.data, gdi);
} }
@ -784,6 +900,12 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) {
else if (lbi.id == "OpenList") { else if (lbi.id == "OpenList") {
enableOpen(gdi); enableOpen(gdi);
} }
else if (lbi.id == "Runner") {
currentRunnerId = lbi.getDataInt();
gdi.restore("EditList", false);
renderListPreview(gdi);
gdi.refresh();
}
} }
else if (type==GUI_CLEAR) { else if (type==GUI_CLEAR) {
return checkSave(gdi); return checkSave(gdi);
@ -845,6 +967,26 @@ bool ListEditor::saveListPost(gdioutput &gdi, MetaListPost &mlp) {
return force; return force;
} }
bool ListEditor::saveImage(gdioutput& gdi, MetaListPost& mlp) {
ListBoxInfo lbi;
int selIx = gdi.getSelectedItem("Image").first;
if (selIx >= 0) {
auto imgId = image.getIdFromEnumeration(selIx);
mlp.setText(itow(imgId));
mlp.setImageDimension(gdi.getTextNo("ImgWidth"), gdi.getTextNo("ImgHeight"));
mlp.setImageOffset(gdi.getTextNo("ImgOffsetX"), gdi.getTextNo("ImgOffsetY"));
mlp.setImageStyle(gdi.isChecked("TransparentWhite") ? 1 : 0);
mlp.imageUnderText(gdi.isChecked("ImageUnderText"));
}
else {
mlp.setText(L"");
}
makeDirty(gdi, MakeDirty, MakeDirty);
return false;
}
int ListEditor::readLeg(gdioutput &gdi, EPostType newType, bool checkError) const { int ListEditor::readLeg(gdioutput &gdi, EPostType newType, bool checkError) const {
if (MetaList::isResultModuleOutput(newType)) { if (MetaList::isResultModuleOutput(newType)) {
int leg = gdi.getTextNo("Leg"); int leg = gdi.getTextNo("Leg");
@ -863,6 +1005,13 @@ int ListEditor::readLeg(gdioutput &gdi, EPostType newType, bool checkError) cons
else else
return -1; return -1;
} }
else if (MetaList::isAllLegType(newType)) {
int leg = gdi.getSelectedItem("LegSel").first;
if (leg >= 0)
return leg - 1; // -1 -> automatic
else
return -2; // All legs
}
else { else {
if (gdi.isChecked("UseLeg")) { if (gdi.isChecked("UseLeg")) {
int leg = gdi.getTextNo("Leg"); int leg = gdi.getTextNo("Leg");
@ -899,7 +1048,7 @@ void ListEditor::updateType(int iType, gdioutput & gdi) {
if (gdi.getText("Leg").empty()) if (gdi.getText("Leg").empty())
gdi.setText("Leg", L"0"); gdi.setText("Leg", L"0");
} }
else if (MetaList::isAllStageType(type)) { else if (MetaList::isAllStageType(type) || MetaList::isAllLegType(type)) {
} }
else { else {
@ -915,23 +1064,36 @@ void ListEditor::updateType(int iType, gdioutput & gdi) {
showExample(gdi, type); showExample(gdi, type);
} }
void ListEditor::checkUnsaved(gdioutput &gdi) { bool ListEditor::checkUnsaved(gdioutput& gdi) {
if (gdi.hasData("IsEditing")) { if (gdi.hasData("IsEditing")) {
if (gdi.hasData("CurrentId")) { DWORD id;
DWORD id; gdi.getData("CurrentId", id);
gdi.getData("CurrentId", id); int groupIx, lineIx, ix;
int groupIx, lineIx, ix; getPosFromId(id, groupIx, lineIx, ix);
getPosFromId(id, groupIx, lineIx, ix); MetaListPost& mlp = currentList->getMLP(groupIx, lineIx, ix);
MetaListPost &mlp = currentList->getMLP(groupIx, lineIx, ix); return saveListPost(gdi, mlp);
saveListPost(gdi, mlp);
}
} }
if (gdi.hasData("IsEditingList")) { else if (gdi.hasData("IsEditingImage")) {
DWORD id;
gdi.getData("CurrentId", id);
int groupIx, lineIx, ix;
getPosFromId(id, groupIx, lineIx, ix);
MetaListPost& mlp = currentList->getMLP(groupIx, lineIx, ix);
return saveImage(gdi, mlp);
}
else if (gdi.hasData("IsEditingList")) {
if (gdi.isInputChanged("")) { if (gdi.isInputChanged("")) {
gdi.setData("NoRedraw", 1); gdi.setData("NoRedraw", 1);
gdi.sendCtrlMessage("ApplyListProp"); gdi.sendCtrlMessage("ApplyListProp");
} }
} }
else if (gdi.hasData("IsSplitListEdit")) {
if (gdi.isInputChanged("")) {
gdi.setData("NoRedraw", 1);
gdi.sendCtrlMessage("ApplySplitList");
}
}
return false;
} }
void ListEditor::updateAlign(gdioutput &gdi, int val) { void ListEditor::updateAlign(gdioutput &gdi, int val) {
@ -949,38 +1111,17 @@ void ListEditor::updateAlign(gdioutput &gdi, int val) {
} }
void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) { void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) {
checkUnsaved(gdi);
gdi.restore("EditList", false);
gdi.dropLine();
gdi.enableInput("EditList");
int groupIx, lineIx, ix; int groupIx, lineIx, ix;
getPosFromId(id, groupIx, lineIx, ix); getPosFromId(id, groupIx, lineIx, ix);
const bool hasResultModule = currentList && !currentList->getResultModule().empty();
int x1 = gdi.getCX();
int y1 = gdi.getCY();
int margin = gdi.scaleLength(10);
gdi.setCX(x1+margin);
gdi.dropLine(); int x1, y1, boxY;
gdi.pushX(); editDlgStart(gdi, id, "Listpost", x1, y1, boxY);
gdi.fillRight();
gdi.addString("", boldLarge, "Listpost").setColor(colorDarkGrey);
gdi.setCX(gdi.getCX() + gdi.scaleLength(20));
gdi.addButton("MoveLeft", "<< Flytta vänster", editListCB).setExtra(id-1); int maxX = gdi.getCX();
if (ix == 0)
gdi.setInputStatus("MoveLeft", false);
gdi.addButton("MoveRight", "Flytta höger >>", editListCB).setExtra(id+1);
if (ix + 1 == currentList->getNumPostsOnLine(groupIx, lineIx))
gdi.setInputStatus("MoveRight", false);
gdi.dropLine(1);
int boxY = gdi.getCY();
gdi.dropLine(2);
gdi.popX(); gdi.popX();
const bool hasResultModule = currentList && !currentList->getResultModule().empty();
vector< pair<wstring, size_t> > types; vector< pair<wstring, size_t> > types;
int currentType; int currentType;
mlp.getTypes(types, currentType); mlp.getTypes(types, currentType);
@ -1039,22 +1180,7 @@ void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) {
int leg = mlp.getLeg(); int leg = mlp.getLeg();
legStageTypeIndex(gdi, storedType, leg); legStageTypeIndex(gdi, storedType, leg);
/*gdi.addCheckbox(xpUseLeg, ypUseLeg, "UseLeg", getIndexDescription(storedType), editListCB, leg != -1);
//gdi.dropLine(-0.2);
int dx = gdi.scaleLength(250);
int dy = -gdi.getLineHeight() / 5;
//gdi.setCX(gdi.getCX() + gdi.scaleLength(100));
if (MetaList::isResultModuleOutput(storedType))
gdi.addInput(xpUseLeg + dx, ypUseLeg + dy, "Leg", leg >= 0 ? itow(leg) : L"0", 4);
else
gdi.addInput(xpUseLeg + dx, ypUseLeg + dy, "Leg", leg >= 0 ? itow(leg + 1) : L"", 4);
gdi.setInputStatus("Leg", leg != -1);
*/
if (MetaList::isResultModuleOutput(storedType)) { if (MetaList::isResultModuleOutput(storedType)) {
//gdi.check("UseLeg", true);
//gdi.disableInput("UseLeg");
if (gdi.hasWidget("UseResultModule")) { if (gdi.hasWidget("UseResultModule")) {
gdi.check("UseResultModule", true); gdi.check("UseResultModule", true);
gdi.disableInput("UseResultModule"); gdi.disableInput("UseResultModule");
@ -1083,9 +1209,6 @@ void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) {
gdi.dropLine(1.9); gdi.dropLine(1.9);
gdi.popX(); gdi.popX();
gdi.fillRight(); gdi.fillRight();
gdi.dropLine(2); gdi.dropLine(2);
int maxY = gdi.getCY(); int maxY = gdi.getCY();
@ -1115,7 +1238,7 @@ void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) {
gdi.addInput("MinIndent", itow(mlp.getMinimalIndent()), 7, 0, L"Justering i sidled:"); gdi.addInput("MinIndent", itow(mlp.getMinimalIndent()), 7, 0, L"Justering i sidled:");
int maxX = gdi.getCX(); maxX = max(maxX, gdi.getCX());
gdi.popX(); gdi.popX();
gdi.dropLine(3); gdi.dropLine(3);
vector< pair<wstring, size_t> > fonts; vector< pair<wstring, size_t> > fonts;
@ -1136,8 +1259,6 @@ void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) {
gdi.addItem("TextAdjust", lang.tl("Centrera"), textCenter); gdi.addItem("TextAdjust", lang.tl("Centrera"), textCenter);
gdi.selectItemByData("TextAdjust", mlp.getTextAdjustNum()); gdi.selectItemByData("TextAdjust", mlp.getTextAdjustNum());
//gdi.popX();
//gdi.dropLine(2);
gdi.dropLine(); gdi.dropLine();
gdi.addButton("Color", "Färg...", editListCB).setExtra(mlp.getColorValue()); gdi.addButton("Color", "Färg...", editListCB).setExtra(mlp.getColorValue());
@ -1190,6 +1311,166 @@ void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) {
gdi.refresh(); gdi.refresh();
} }
void ListEditor::editDlgStart(gdioutput& gdi, int id, const char *title, int &x1, int &y1, int &boxY) {
int groupIx, lineIx, ix;
getPosFromId(id, groupIx, lineIx, ix);
checkUnsaved(gdi);
gdi.restore("EditList", false);
gdi.dropLine();
gdi.enableInput("EditList");
gdi.enableInput("SplitPrint");
x1 = gdi.getCX();
y1 = gdi.getCY();
int margin = gdi.scaleLength(10);
gdi.setCX(x1 + margin);
gdi.dropLine();
gdi.pushX();
gdi.fillRight();
gdi.addString("", boldLarge, title).setColor(colorDarkGrey);
gdi.setCX(gdi.getCX() + gdi.scaleLength(20));
gdi.addButton("MoveLeft", "<< Flytta vänster", editListCB).setExtra(id - 1);
if (ix == 0)
gdi.setInputStatus("MoveLeft", false);
gdi.addButton("MoveRight", "Flytta höger >>", editListCB).setExtra(id + 1);
if (ix + 1 == currentList->getNumPostsOnLine(groupIx, lineIx))
gdi.setInputStatus("MoveRight", false);
gdi.dropLine(1);
boxY = gdi.getCY();
gdi.dropLine(2);
}
void ListEditor::editImage(gdioutput& gdi, const MetaListPost& mlp, int id) {
int groupIx, lineIx, ix;
getPosFromId(id, groupIx, lineIx, ix);
uint64_t imgId = mlp.getImageId();
int x1, y1, boxY;
editDlgStart(gdi, id, "Bild", x1, y1, boxY);
int maxX = gdi.getCX();
gdi.popX();
gdi.fillRight();
gdi.dropLine(1);
gdi.addSelection("Image", 200, 200, editListCB, L"Välj bild:", L"Välj bland befintliga bilder");
int selIx = selectImage(gdi, imgId);
gdi.dropLine(1);
gdi.addButton("NewImage", "Ny bild...", editListCB);
int maxY = 0;
maxX = max(maxX, gdi.getCX());
int innerBoxLowerCX = maxX + gdi.scaleLength(6);
maxX += gdi.scaleLength(12);
gdi.popX();
gdi.dropLine(3);
wstring wh, ww, xoff, yoff;
bool keepRatio = true;
if (imgId) {
int h = mlp.getImageHeight();
int w = mlp.getImageWidth();
wh = itow(h);
ww = itow(w);
int hImg = image.getHeight(imgId);
int wImg = image.getWidth(imgId);
int hComputed = (w * hImg + wImg / 2) / wImg;
int wComputed = (h * wImg + hImg / 2) / hImg;
keepRatio = std::abs(h - hComputed) <= 1 || std::abs(w - wComputed) <= 1;
xoff = itow(mlp.getImageOffsetX());
yoff = itow(mlp.getImageOffsetY());
}
gdi.addInput("ImgWidth", ww, 5, editListCB, L"Bredd:");
gdi.addInput("ImgHeight", wh, 5, editListCB, L"Höjd:");
gdi.dropLine();
gdi.addCheckbox("PreserveAspectRatio", "Bevara höjd/bredd-relationen", nullptr, keepRatio);
gdi.popX();
gdi.dropLine(2);
gdi.dropLine(1);
gdi.addString("", 0, "Förskjutning:");
gdi.dropLine(-1);
gdi.addInput("ImgOffsetX", xoff, 5, editListCB, L"Horizontell:");
gdi.addInput("ImgOffsetY", yoff, 5, editListCB, L"Vertikal:");
gdi.popX();
gdi.dropLine(3);
gdi.addCheckbox("TransparentWhite", "Tolka vitt som genomskinligt", editListCB, mlp.getImageStyle() == 1);
gdi.addCheckbox("ImageUnderText", "Bild under text", editListCB, mlp.imageUnderText());
bool hasImg = imgId != 0;
gdi.setInputStatus("ImgWidth", hasImg);
gdi.setInputStatus("ImgHeight", hasImg);
gdi.setInputStatus("ImgOffsetX", hasImg);
gdi.setInputStatus("ImgOffsetY", hasImg);
gdi.setInputStatus("PreserveAspectRatio", hasImg);
gdi.dropLine(3);
gdi.setData("CurrentId", id);
gdi.addButton("Remove", "Radera", editListCB, "Ta bort listposten");
gdi.addButton("Cancel", "Avbryt", editListCB).setCancel();
gdi.updatePos(gdi.getCX(), gdi.getCY(), gdi.scaleLength(20), 0);
gdi.addButton("Apply", "OK", editListCB).setDefault();
gdi.dropLine(1);
maxY = max(maxY, gdi.getCY());
maxX = max(gdi.getCX(), maxX);
RECT rc;
rc.top = y1;
rc.left = x1;
rc.right = maxX + gdi.scaleLength(6);
rc.bottom = maxY + gdi.scaleLength(6) + gdi.getLineHeight();
gdi.addRectangle(rc, colorLightBlue, true, false);
gdi.setData("IsEditingImage", 1);
gdi.scrollToBottom();
gdi.setCX(rc.right + gdi.scaleLength(10));
gdi.setCY(boxY);
if (imgId != 0) {
previewImage(gdi, selIx);
}
else
gdi.refresh();
}
int ListEditor::selectImage(gdioutput &gdi, uint64_t imgId) {
vector<pair<wstring, size_t>> img;
image.enumerateImages(img);
img.emplace(img.begin(), lang.tl("Ingen[bild]"), -2);
gdi.addItem("Image", img);
int ix = image.getEnumerationIxFromId(imgId);
if (ix >= 0)
gdi.selectItemByData("Image", ix);
else
gdi.selectFirstItem("Image");
return ix;
}
void ListEditor::showExample(gdioutput &gdi, EPostType type) { void ListEditor::showExample(gdioutput &gdi, EPostType type) {
if (type == EPostType::lLastItem) { if (type == EPostType::lLastItem) {
type = EPostType(gdi.getSelectedItem("Type").first); type = EPostType(gdi.getSelectedItem("Type").first);
@ -1275,6 +1556,46 @@ void ListEditor::showExample(gdioutput &gdi, const MetaListPost &mlp) {
gdi.addRectangle(rrInner, color, true); gdi.addRectangle(rrInner, color, true);
} }
void ListEditor::previewImage(gdioutput& gdi, int data) const {
gdi.restoreNoUpdate("image_preview");
gdi.setRestorePoint("image_preview");
if (data >= 0) {
auto imgId = image.getIdFromEnumeration(data);
oe->loadImage(imgId);
bool transparent = gdi.isChecked("TransparentWhite");
image.reloadImage(imgId, transparent ? Image::ImageMethod::WhiteTransparent : Image::ImageMethod::Default);
gdi.addImage("", gdi.getCY(), gdi.getCX(), 0, itow(imgId));
}
gdi.refreshFast();
}
void ListEditor::updateImageStatus(gdioutput& gdi, int data) {
bool hasImg = int(data) >= 0;
gdi.setInputStatus("ImgWidth", hasImg);
gdi.setInputStatus("ImgHeight", hasImg);
gdi.setInputStatus("PreserveAspectRatio", hasImg);
gdi.setInputStatus("ImgOffsetX", hasImg);
gdi.setInputStatus("ImgOffsetY", hasImg);
if (hasImg) {
auto imgId = image.getIdFromEnumeration(data);
int h = image.getHeight(imgId);
int w = image.getWidth(imgId);
gdi.setText("ImgWidth", w);
gdi.setText("ImgHeight", h);
if (gdi.getTextNo("ImgOffsetX") == 0 && gdi.getTextNo("ImgOffsetY") == 0) {
// Change from blank to "0"
gdi.setText("ImgOffsetX", 0);
gdi.setText("ImgOffsetY", 0);
}
}
else {
gdi.setText("ImgWidth", L"");
gdi.setText("ImgHeight", L"");
}
}
const wchar_t *ListEditor::getIndexDescription(EPostType type) { const wchar_t *ListEditor::getIndexDescription(EPostType type) {
if (type == lResultModuleTime || type == lResultModuleTimeTeam) if (type == lResultModuleTime || type == lResultModuleTimeTeam)
return L"Index in X[index]#OutputTimes"; return L"Index in X[index]#OutputTimes";
@ -1310,7 +1631,7 @@ bool ListEditor::legStageTypeIndex(gdioutput &gdi, EPostType type, int leg) {
gdi.setText("Leg", legW); gdi.setText("Leg", legW);
} }
} }
else if (MetaList::isAllStageType(type)) { else if (MetaList::isAllStageType(type) || MetaList::isAllLegType(type)) {
if (gdi.hasWidget("UseLeg")) if (gdi.hasWidget("UseLeg"))
gdi.removeWidget("UseLeg"); gdi.removeWidget("UseLeg");
@ -1322,9 +1643,22 @@ bool ListEditor::legStageTypeIndex(gdioutput &gdi, EPostType type, int leg) {
else else
gdi.addString("TUseLeg", ypUseLeg, xpUseLeg, 0, getIndexDescription(type)); gdi.addString("TUseLeg", ypUseLeg, xpUseLeg, 0, getIndexDescription(type));
if (!gdi.hasWidget("LegSel")) { if (!gdi.hasWidget("LegSel"))
gdi.addSelection(xpUseLeg + dx, ypUseLeg + dy, "LegSel", 160, gdi.scaleLength(300), editListCB); gdi.addSelection(xpUseLeg + dx, ypUseLeg + dy, "LegSel", 160, gdi.scaleLength(300), editListCB);
vector<pair<wstring, size_t>> items; vector<pair<wstring, size_t>> items;
if (MetaList::isAllLegType(type)) {
items.emplace_back(lang.tl("Automatisk"), 0);
items.emplace_back(lang.tl("Alla sträckor"), -2);
for (int j = 1; j <= 50; j++) {
items.emplace_back(lang.tl("Sträcka X#" + itos(j)), j);
}
gdi.addItem("LegSel", items);
if (leg >= -1)
gdi.selectItemByData("LegSel", leg + 1);
else if (leg == -2)
gdi.selectItemByData("LegSel", -2);
}
else {
items.emplace_back(lang.tl("Alla tidigare etapper"), -2); items.emplace_back(lang.tl("Alla tidigare etapper"), -2);
for (int j = 1; j < 20; j++) { for (int j = 1; j < 20; j++) {
items.emplace_back(lang.tl("Etapp X#" + itos(j)), j); items.emplace_back(lang.tl("Etapp X#" + itos(j)), j);
@ -1373,6 +1707,7 @@ void ListEditor::editListProp(gdioutput &gdi, bool newList) {
if (!newList) { if (!newList) {
gdi.restore("EditList", false); gdi.restore("EditList", false);
gdi.disableInput("EditList"); gdi.disableInput("EditList");
gdi.disableInput("SplitPrint");
} }
gdi.dropLine(0.8); gdi.dropLine(0.8);
@ -1552,6 +1887,80 @@ void ListEditor::editListProp(gdioutput &gdi, bool newList) {
gdi.setInputFocus("Name"); gdi.setInputFocus("Name");
} }
void ListEditor::statusSplitPrint(gdioutput& gdi, bool status) {
gdi.setInputStatus("Split", status);
gdi.setInputStatus("Speed", status);
gdi.setInputStatus("Result", status);
gdi.setInputStatus("Analysis", status);
}
void ListEditor::splitPrintList(gdioutput& gdi) {
checkUnsaved(gdi);
if (!currentList)
return;
MetaList& list = *currentList;
gdi.restore("EditList", false);
gdi.disableInput("EditList");
gdi.disableInput("SplitPrint");
gdi.dropLine(0.8);
int x1 = gdi.getCX();
int y1 = gdi.getCY();
int margin = gdi.scaleLength(10);
gdi.setCX(x1 + margin);
gdi.dropLine();
gdi.fillDown();
gdi.addString("", boldLarge, "Sträcktidsutskrift").setColor(colorDarkGrey);
gdi.dropLine();
gdi.fillRight();
gdi.pushX();
bool isSP = list.isSplitPrintList();
auto sp = list.getSplitPrintInfo();
gdi.fillDown();
gdi.addCheckbox("UseForSplit", "Använd listan för sträcktidsutskrift", editListCB, isSP);
gdi.dropLine(0.5);
gdi.fillRight();
gdi.addCheckbox("Split", "Inkludera sträcktider", nullptr, isSP ? sp->includeSplitTimes : true);
gdi.addCheckbox("Speed", "Inkludera tempo", nullptr, isSP ? sp->withSpeed : true);
gdi.addCheckbox("Result", "Inkludera individuellt resultat", nullptr, isSP ? sp->withResult : true);
gdi.addCheckbox("Analysis", "Inkludera bomanalys", nullptr, isSP ? sp->withAnalysis : true);
statusSplitPrint(gdi, isSP);
gdi.dropLine(0.8);
gdi.setCX(gdi.getCX() + 20);
gdi.addButton("ApplySplitList", "OK", editListCB);
gdi.addButton("Cancel", "Avbryt", editListCB);
gdi.dropLine(3);
int maxY = gdi.getCY();
int maxX = gdi.getCX();
gdi.fillDown();
gdi.popX();
gdi.setData("IsSplitListEdit", 1);
RECT rc;
rc.top = y1;
rc.left = x1;
rc.right = maxX + gdi.scaleLength(6);
rc.bottom = maxY;
gdi.addRectangle(rc, colorLightBlue, true);
gdi.scrollToBottom();
gdi.refresh();
}
void ListEditor::makeDirty(gdioutput &gdi, DirtyFlag inside, DirtyFlag outside) { void ListEditor::makeDirty(gdioutput &gdi, DirtyFlag inside, DirtyFlag outside) {
if (inside == MakeDirty) if (inside == MakeDirty)
dirtyInt = true; dirtyInt = true;
@ -1575,10 +1984,10 @@ void ListEditor::makeDirty(gdioutput &gdi, DirtyFlag inside, DirtyFlag outside)
bool ListEditor::checkSave(gdioutput &gdi) { bool ListEditor::checkSave(gdioutput &gdi) {
if (dirtyInt || dirtyExt) { if (dirtyInt || dirtyExt) {
gdioutput::AskAnswer answer = gdi.askCancel(L"Vill du spara ändringar?"); gdioutput::AskAnswer answer = gdi.askCancel(L"Vill du spara ändringar?");
if (answer == gdioutput::AnswerCancel) if (answer == gdioutput::AskAnswer::AnswerCancel)
return false; return false;
if (answer == gdioutput::AnswerYes) { if (answer == gdioutput::AskAnswer::AnswerYes) {
if (currentIndex >= 0) if (currentIndex >= 0)
gdi.sendCtrlMessage("SaveInside"); gdi.sendCtrlMessage("SaveInside");
else if (gdi.sendCtrlMessage("SaveFile") == 0) else if (gdi.sendCtrlMessage("SaveFile") == 0)

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -43,6 +43,7 @@ private:
MetaList *currentList; MetaList *currentList;
void setCurrentList(MetaList *lst); void setCurrentList(MetaList *lst);
int currentIndex; int currentIndex;
int currentRunnerId = 0;
wstring savedFileName; wstring savedFileName;
bool dirtyExt; bool dirtyExt;
bool dirtyInt; bool dirtyInt;
@ -54,12 +55,22 @@ private:
void updateType(int iType, gdioutput &gdi); void updateType(int iType, gdioutput &gdi);
bool saveListPost(gdioutput &gdi, MetaListPost &mlp); bool saveListPost(gdioutput &gdi, MetaListPost &mlp);
bool saveImage(gdioutput& gdi, MetaListPost& mlp);
static int selectImage(gdioutput& gdi, uint64_t imgId);
void updateImageStatus(gdioutput& gdi, int data);
ButtonInfo &addButton(gdioutput &gdi, const MetaListPost &mlp, int x, int y, ButtonInfo &addButton(gdioutput &gdi, const MetaListPost &mlp, int x, int y,
int lineIx, int ix) const; int lineIx, int ix) const;
void ListEditor::editDlgStart(gdioutput& gdi, int id, const char* title, int& x1, int& y1, int& boxY);
void editListPost(gdioutput &gdi, const MetaListPost &mlp, int id); void editListPost(gdioutput &gdi, const MetaListPost &mlp, int id);
void editImage(gdioutput& gdi, const MetaListPost& mlp, int id);
void previewImage(gdioutput &gdi, int data) const;
void showExample(gdioutput &gdi, EPostType type = EPostType::lLastItem); void showExample(gdioutput &gdi, EPostType type = EPostType::lLastItem);
void showExample(gdioutput &gdi, const MetaListPost &mlp); void showExample(gdioutput &gdi, const MetaListPost &mlp);
@ -68,10 +79,14 @@ private:
void editListProp(gdioutput &gdi, bool newList); void editListProp(gdioutput &gdi, bool newList);
void statusSplitPrint(gdioutput& gdi, bool status);
void splitPrintList(gdioutput& gdi);
enum DirtyFlag {MakeDirty, ClearDirty, NoTouch}; enum DirtyFlag {MakeDirty, ClearDirty, NoTouch};
/// Check (and autosave) if there are unsaved changes in a dialog box /// Check (and autosave) if there are unsaved changes in a dialog box. Return force flag
void checkUnsaved(gdioutput &gdi); bool checkUnsaved(gdioutput &gdi);
/// Check and ask if there are changes to save /// Check and ask if there are changes to save
bool checkSave(gdioutput &gdi); bool checkSave(gdioutput &gdi);
@ -91,6 +106,7 @@ private:
bool legStageTypeIndex(gdioutput &gdi, EPostType type, int leg); bool legStageTypeIndex(gdioutput &gdi, EPostType type, int leg);
void renderListPreview(gdioutput& gdi);
public: public:
ListEditor(oEvent *oe); ListEditor(oEvent *oe);

View File

@ -1,6 +1,6 @@
/********************i**************************************************** /********************i****************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -89,10 +89,21 @@ void LiveResult::showTimer(gdioutput &gdi, const oListInfo &liIn) {
gdi.setData("PunchSync", 1); gdi.setData("PunchSync", 1);
gdi.setRestorePoint("LiveResult"); gdi.setRestorePoint("LiveResult");
readoutResult();
resYPos = h/3;
calculateResults();
showResultList = 0;
gdi.addTimeoutMilli(1000, "res", 0).setHandler(this);
gdi.refreshFast();
}
void LiveResult::readoutResult() {
lastTime = 0; lastTime = 0;
vector<const oFreePunch *> pp; vector<const oFreePunch*> pp;
oe->synchronizeList({ oListId::oLRunnerId, oListId::oLPunchId }); oe->synchronizeList({ oListId::oLRunnerId, oListId::oLPunchId });
oe->getLatestPunches(lastTime, pp); oe->getLatestPunches(lastTime, pp);
processedPunches.clear(); processedPunches.clear();
@ -103,14 +114,14 @@ void LiveResult::showTimer(gdioutput &gdi, const oListInfo &liIn) {
fromPunch = oPunch::PunchStart; fromPunch = oPunch::PunchStart;
if (toPunch == 0) if (toPunch == 0)
toPunch = oPunch::PunchFinish; toPunch = oPunch::PunchFinish;
for (size_t k = 0; k < pp.size(); k++) { for (size_t k = 0; k < pp.size(); k++) {
lastTime = max(pp[k]->getModificationTime(), lastTime); lastTime = max(pp[k]->getModificationTime(), lastTime);
pRunner r = pp[k]->getTiedRunner(); pRunner r = pp[k]->getTiedRunner();
if (r) { if (r) {
pair<int, int> key = make_pair(r->getId(), pp[k]->getControlId()); pair<int, int> key = make_pair(r->getId(), pp[k]->getControlId());
processedPunches[key] = max(processedPunches[key], pp[k]->getAdjustedTime()); processedPunches[key] = max(processedPunches[key], pp[k]->getAdjustedTime());
if (!li.getParam().selection.empty() && !li.getParam().selection.count(r->getClassId(true))) if (!li.getParam().selection.empty() && !li.getParam().selection.count(r->getClassId(true)))
continue; // Filter class continue; // Filter class
@ -125,9 +136,9 @@ void LiveResult::showTimer(gdioutput &gdi, const oListInfo &liIn) {
startFinishTime.clear(); startFinishTime.clear();
results.clear(); results.clear();
for (map<int, pair<vector<int>, vector<int> > >::iterator it = storedPunches.begin(); for (map<int, pair<vector<int>, vector<int> > >::iterator it = storedPunches.begin();
it != storedPunches.end(); ++it) { it != storedPunches.end(); ++it) {
vector<int> &froms = it->second.first; vector<int>& froms = it->second.first;
vector<int> &tos = it->second.second; vector<int>& tos = it->second.second;
pRunner r = oe->getRunner(it->first, 0); pRunner r = oe->getRunner(it->first, 0);
for (size_t j = 0; j < tos.size(); j++) { for (size_t j = 0; j < tos.size(); j++) {
int fin = pp[tos[j]]->getAdjustedTime(); int fin = pp[tos[j]]->getAdjustedTime();
@ -141,22 +152,15 @@ void LiveResult::showTimer(gdioutput &gdi, const oListInfo &liIn) {
} }
} }
if (time < 100000000 && r->getStatus() <= StatusOK) { if (time < 100000000 && r->getStatus() <= StatusOK) {
// results.push_back(Result()); // results.push_back(Result());
// results.back().r = r; // results.back().r = r;
// results.back().time = time; // results.back().time = time;
startFinishTime[r->getId()].first = sta; startFinishTime[r->getId()].first = sta;
startFinishTime[r->getId()].second = fin; startFinishTime[r->getId()].second = fin;
} }
} }
} }
resYPos = h/3;
calculateResults();
showResultList = 0;
gdi.addTimeoutMilli(1000, "res", 0).setHandler(this);
gdi.refreshFast();
} }
@ -352,20 +356,41 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) {
if (doRefresh) if (doRefresh)
gdi.refreshFast(); gdi.refreshFast();
else {
auto resCopy = results;
calculateResults();
bool reshow = false;
if (resCopy.size() != results.size())
reshow = true;
else {
for (size_t i = 0; i < results.size(); i++) {
if (resCopy[i].name.empty())
break;
if (resCopy[i].runnerId != results[i].runnerId) {
reshow = true;
break;
}
if (resCopy[i].time != results[i].time) {
reshow = true;
break;
}
pRunner r = oe->getRunner(results[i].runnerId, 0);
if (!r || resCopy[i].name != r->getName()) {
reshow = true;
break;
}
}
}
if (reshow) {
showResults(gdi);
}
}
} }
else if (type == GUI_TIMEOUT) { else if (type == GUI_TIMEOUT) {
gdi.restore("LiveResult", false); showResults(gdi);
int h,w;
gdi.getTargetDimension(w, h);
gdi.fillDown();
BaseInfo *bi = gdi.setTextTranslate("timing", L"MeOS Timing", false);
TextInfo &ti = dynamic_cast<TextInfo &>(*bi);
ti.changeFont(getFont(gdi, 0.7));
gdi.refreshFast();
resYPos = ti.textRect.bottom + gdi.scaleLength(20);
calculateResults();
showResultList = 0;
gdi.addTimeoutMilli(300, "res", 0).setHandler(this);
} }
else if (type == GUI_TIMER) { else if (type == GUI_TIMER) {
if (size_t(showResultList) >= results.size()) if (size_t(showResultList) >= results.size())
@ -379,9 +404,10 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) {
gdi.addTimeoutMilli(10, "res" + itos(showResultList), 0).setHandler(this); gdi.addTimeoutMilli(10, "res" + itos(showResultList), 0).setHandler(this);
} }
else if (res.place > 0) { else if (res.place > 0) {
res.name = r->getName();
int h,w; int h,w;
gdi.getTargetDimension(w, h); gdi.getTargetDimension(w, h);
gdi.takeShownStringsSnapshot(); gdi.takeShownStringsSnapshot();
TextInfo &ti = gdi.addStringUT(y, 30, fontLarge, itow(res.place) + L".", 0, 0, font.c_str()); TextInfo &ti = gdi.addStringUT(y, 30, fontLarge, itow(res.place) + L".", 0, 0, font.c_str());
int ht = ti.textRect.bottom - ti.textRect.top; int ht = ti.textRect.bottom - ti.textRect.top;
@ -400,6 +426,22 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) {
} }
} }
void LiveResult::showResults(gdioutput &gdi) {
gdi.restore("LiveResult", false);
int h, w;
gdi.getTargetDimension(w, h);
gdi.fillDown();
BaseInfo* bi = gdi.setTextTranslate("timing", L"MeOS Timing", false);
TextInfo& ti = dynamic_cast<TextInfo&>(*bi);
ti.changeFont(getFont(gdi, 0.7));
gdi.refreshFast();
resYPos = ti.textRect.bottom + gdi.scaleLength(20);
calculateResults();
showResultList = 0;
gdi.addTimeoutMilli(300, "res", 0).setHandler(this);
}
void LiveResult::calculateResults() { void LiveResult::calculateResults() {
rToWatch.clear(); rToWatch.clear();
results.clear(); results.clear();

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -49,14 +49,17 @@ class LiveResult : public GuiHandler {
int place; int place;
int runnerId; int runnerId;
int time; int time;
wstring name;
bool operator<(const Result &b) const { bool operator<(const Result &b) const {
return time < b.time; return time < b.time;
} }
}; };
vector< Result > results; vector<Result> results;
void calculateResults(); void calculateResults();
void readoutResult();
void showResults(gdioutput& gdi);
public: public:
LiveResult(oEvent *oe); LiveResult(oEvent *oe);

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -54,7 +54,7 @@ void MachineContainer::AbstractMachine::load(const xmlobject &data) {
xmlList out; xmlList out;
data.getObjects(out); data.getObjects(out);
for (auto &x : out) { for (auto &x : out) {
props[x.getName()] = x.getw(); props[x.getName()] = x.getWStr();
} }
} }

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -1832,14 +1832,13 @@ bool getMeOSFile(wchar_t *FileNamePath, const wchar_t *FileName) {
return true; return true;
} }
bool getUserFile(wchar_t *FileNamePath, const wchar_t *FileName) bool getUserFile(wchar_t* FileNamePath, const wchar_t* FileName) {
{
wchar_t Path[MAX_PATH]; wchar_t Path[MAX_PATH];
wchar_t AppPath[MAX_PATH]; wchar_t AppPath[MAX_PATH];
if (SHGetSpecialFolderPath(hWndMain, Path, CSIDL_APPDATA, 1)!=NOERROR) { if (SHGetSpecialFolderPath(hWndMain, Path, CSIDL_APPDATA, 1) != NOERROR) {
int i=wcslen(Path); int i = wcslen(Path);
if (Path[i-1]!='\\') if (Path[i - 1] != '\\')
wcscat_s(Path, MAX_PATH, L"\\"); wcscat_s(Path, MAX_PATH, L"\\");
wcscpy_s(AppPath, MAX_PATH, Path); wcscpy_s(AppPath, MAX_PATH, Path);

View File

@ -52,6 +52,7 @@ IDB_ECO BITMAP "bmp00001.bmp"
IDI_SPLASHIMAGE PNG "meos.png" IDI_SPLASHIMAGE PNG "meos.png"
IDI_MEOSIMAGE PNG "title.png" IDI_MEOSIMAGE PNG "title.png"
IDI_MEOSINFO PNG "info24.png"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// English (United States) resources // English (United States) resources

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -152,7 +152,7 @@ int getRelativeDay() {
return int(qp); return int(qp);
} }
__int64 SystemTimeToInt64Second(const SYSTEMTIME &st) { __int64 SystemTimeToInt64TenthSecond(const SYSTEMTIME &st) {
FILETIME ft; FILETIME ft;
SystemTimeToFileTime(&st, &ft); SystemTimeToFileTime(&st, &ft);
@ -160,16 +160,16 @@ __int64 SystemTimeToInt64Second(const SYSTEMTIME &st) {
u.HighPart = ft.dwHighDateTime; u.HighPart = ft.dwHighDateTime;
u.LowPart = ft.dwLowDateTime; u.LowPart = ft.dwLowDateTime;
__int64 qp = u.QuadPart; __int64 qp = u.QuadPart;
qp /= __int64(10) * 1000 * 1000; qp /= __int64(1000 * 1000 * timeUnitsPerSecond);
return qp; return qp;
} }
SYSTEMTIME Int64SecondToSystemTime(__int64 time) { SYSTEMTIME Int64TenthSecondToSystemTime(__int64 time) {
SYSTEMTIME st; SYSTEMTIME st;
FILETIME ft; FILETIME ft;
ULARGE_INTEGER u; ULARGE_INTEGER u;
u.QuadPart = time * __int64(10) * 1000 * 1000; u.QuadPart = time * __int64(1000 * 1000 * timeUnitsPerSecond);
ft.dwHighDateTime = u.HighPart; ft.dwHighDateTime = u.HighPart;
ft.dwLowDateTime = u.LowPart; ft.dwLowDateTime = u.LowPart;
@ -257,44 +257,87 @@ int convertDateYMS(const wstring &m, SYSTEMTIME &st, bool checkValid) {
return convertDateYMS(ms, st, checkValid); return convertDateYMS(ms, st, checkValid);
} }
//Absolute time string to SYSTEM TIME //Absolute time string to SYSTEM TIME
int convertDateYMS(const string &m, SYSTEMTIME &st, bool checkValid) { int convertDateYMS(const string& m, SYSTEMTIME& st, bool checkValid) {
memset(&st, 0, sizeof(st)); memset(&st, 0, sizeof(st));
if (m.length()==0) if (m.length() == 0)
return -1; return -1;
int len=m.length(); int len = m.length();
for (int k=0;k<len;k++) { int dashCount = 0;
BYTE b=m[k]; for (int k = 0; k < len; k++) {
BYTE b = m[k];
if (b == 'T') if (b == 'T')
break; break;
if ( !(b=='-' || b==' ' || (b>='0' && b<='9')) ) if (!(b == '-' || b == ' ' || (b >= '0' && b <= '9')))
return -1; return -1;
if (b == '-')
dashCount++;
} }
int year=atoi(m.c_str()); int year = atoi(m.c_str());
if (year<1900 || year>3000)
return -1;
int month=0; if (dashCount == 0) {
int day=0; int day = year % 100;
int kp=m.find_first_of('-'); year /= 100;
int month = year % 100;
year /= 100;
if (kp!=string::npos) { if ((year > 0 && year < 100) || (year == 0 && m.size() > 2 && m[0] == '0' && m[1] == '0'))
string mtext=m.substr(kp+1); year = extendYear(year);
month=atoi(mtext.c_str());
if (month<1 || month>12) { if (year < 1900 || year>3000)
return -1;
if (month < 1 || month>12) {
if (checkValid) if (checkValid)
return -1; return -1;
month = 1; month = 1;
} }
kp=mtext.find_last_of('-'); if (day < 1 || day>31) {
if (checkValid)
return -1;
day = 1;
}
if (kp!=string::npos) { st.wYear = year;
day=atoi(mtext.substr(kp+1).c_str()); st.wMonth = month;
if (day<1 || day>31) { st.wDay = day;
int t = year * 100 * 100 + month * 100 + day;
if (t < 0)
return -1;
return t;
}
if ((year > 0 && year < 100) || (year == 0 && m.size() > 2 && m[0] == '0' && m[1] == '0'))
year = extendYear(year);
if (year < 1900 || year>3000)
return -1;
int month = 0;
int day = 0;
int kp = m.find_first_of('-');
if (kp != string::npos) {
string mtext = m.substr(kp + 1);
month = atoi(mtext.c_str());
if (month < 1 || month>12) {
if (checkValid)
return -1;
month = 1;
}
kp = mtext.find_last_of('-');
if (kp != string::npos) {
day = atoi(mtext.substr(kp + 1).c_str());
if (day < 1 || day>31) {
if (checkValid) if (checkValid)
return -1; return -1;
day = 1; day = 1;
@ -306,8 +349,8 @@ int convertDateYMS(const string &m, SYSTEMTIME &st, bool checkValid) {
st.wDay = day; st.wDay = day;
int t = year*100*100+month*100+day; int t = year * 100 * 100 + month * 100 + day;
if (t<0) return -1; if (t < 0) return -1;
return t; return t;
} }
@ -356,7 +399,7 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) {
return -1; return -1;
if (tpart < daysZeroTime) if (tpart < daysZeroTime)
days--; days--;
return days * 3600 * 24 + tpart; return days * timeConstHour * 24 + tpart;
} }
return -1; return -1;
} }
@ -364,7 +407,7 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) {
int plusIndex = -1; int plusIndex = -1;
for (int k=0;k<len;k++) { for (int k=0;k<len;k++) {
BYTE b=m[k]; BYTE b=m[k];
if ( !(isspace(b) || b==':' || (b>='0' && b<='9')) ) { if ( !(isspace(b) || b==':' || (b>='0' && b<='9') || b == '.' || b == ',') ) {
if (b=='+' && plusIndex ==-1 && k>0) if (b=='+' && plusIndex ==-1 && k>0)
plusIndex = k; plusIndex = k;
else else
@ -377,7 +420,7 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) {
int d = atoi(m.c_str()); int d = atoi(m.c_str());
if (d>0 && t>=0) if (d>0 && t>=0)
return d*24*3600 + t; return d*24* timeConstHour + t;
else else
return -1; return -1;
} }
@ -389,6 +432,7 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) {
int minute=0; int minute=0;
int second=0; int second=0;
int tenth = 0;
int kp=m.find_first_of(':'); int kp=m.find_first_of(':');
if (kp!=string::npos) { if (kp!=string::npos) {
@ -401,13 +445,27 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) {
kp=mtext.find_last_of(':'); kp=mtext.find_last_of(':');
if (kp!=string::npos) { if (kp!=string::npos) {
second=atoi(mtext.substr(kp+1).c_str()); second=atoi(mtext.c_str() + kp+1);
if (second<0 || second>60) if (second<0 || second>60)
second=0; second=0;
if (timeConstSecond > 1) {
kp = mtext.find_last_of('.');
if (kp == string::npos)
kp = mtext.find_last_of(',');
if (kp != string::npos) {
tenth = atoi(mtext.c_str() + kp + 1);
if (tenth < 0 || tenth >= 10)
tenth = 0;
}
}
} }
} }
int t=hour*3600+minute*60+second;
if (t<0) return 0; int t = hour * timeConstHour + minute * timeConstMinute + second * timeConstSecond + tenth;
if (t<0)
return 0;
return t; return t;
} }
@ -477,7 +535,7 @@ int convertAbsoluteTimeISO(const string &m)
if (second<0 || second>60) if (second<0 || second>60)
return -1; return -1;
int t = hour*3600 + minute*60 + second; int t = hour * timeConstHour + minute * timeConstMinute + second;
return t; return t;
} }
@ -509,31 +567,50 @@ int convertAbsoluteTimeMS(const string &m)
mtext=m; mtext=m;
minute=atoi(mtext.c_str()); minute=atoi(mtext.c_str());
int hour = 0;
if (minute<0 || minute>60*24) if (minute<0 || minute>60*24)
minute=0; minute=0;
int kp=mtext.find_first_of(':'); int kp=mtext.find_first_of(':');
bool gotSecond = false;
if (kp!=string::npos) { if (kp!=string::npos) {
mtext = mtext.substr(kp+1); mtext = mtext.substr(kp+1);
second = atoi(mtext.c_str()); second = atoi(mtext.c_str());
gotSecond = true;
if (second<0 || second>60) if (second<0 || second>60)
second=0; second=0;
} }
int t=minute*60+second; int t;
kp = mtext.find_first_of(':');
kp=mtext.find_first_of(':'); if (kp != string::npos) {
if (kp!=string::npos) {
//Allow also for format +-HH:MM:SS //Allow also for format +-HH:MM:SS
mtext = mtext.substr(kp+1); hour = minute;
second=atoi(mtext.c_str()); minute = second;
if (second<0 || second>60)
second=0; mtext = mtext.substr(kp + 1);
else second = atoi(mtext.c_str());
t = t*60 + second; if (second < 0 || second>60)
second = 0;
} }
int tenth = 0;
if (timeConstSecond > 1) {
kp = mtext.find_first_of('.');
if (kp == string::npos)
kp = mtext.find_last_of(',');
if (kp != string::npos) {
tenth = atoi(mtext.c_str() + kp + 1);
if (!gotSecond) { // Reinterpret minute as second (no minute was specified)
second = minute;
minute = 0;
}
if (tenth < 0 || tenth >= 10)
tenth = 0;
}
}
t = hour * timeConstHour + minute * timeConstMinute + second * timeConstSecond + tenth;
return sign*t; return sign*t;
} }
@ -543,13 +620,37 @@ int convertAbsoluteTimeMS(const wstring &m) {
} }
//Generate +-MM:SS or +-HH:MM:SS //Generate +-MM:SS or +-HH:MM:SS
const wstring &getTimeMS(int m) { const wstring &formatTimeMS(int m, bool force2digit, SubSecond mode) {
wchar_t bf[32]; wchar_t bf[32];
int am = abs(m); int am = abs(m);
if (am < 3600 || !MeOSUtil::useHourFormat) if (am < timeConstHour || !MeOSUtil::useHourFormat) {
swprintf_s(bf, L"-%02d:%02d", am/60, am%60); if (force2digit) {
else if (am < 3600*48) if (mode == SubSecond::Off || (mode == SubSecond::Auto && m % 10 == 0))
swprintf_s(bf, L"-%02d:%02d:%02d", am/3600, (am/60)%60, am%60); swprintf_s(bf, L"-%02d:%02d", am / timeConstMinute, (am / timeConstSecond) % 60);
else
swprintf_s(bf, L"-%02d:%02d.%d", am / timeConstMinute, (am / timeConstSecond) % 60, am % timeConstSecond);
}
else {
if (mode == SubSecond::Off || (mode == SubSecond::Auto && m % 10 == 0))
swprintf_s(bf, L"-%d:%02d", am / timeConstMinute, (am / timeConstSecond) % 60);
else
swprintf_s(bf, L"-%d:%02d.%d", am / timeConstMinute, (am / timeConstSecond) % 60, am % timeConstSecond);
}
}
else if (am < timeConstHour * 48) {
if (force2digit) {
if (mode == SubSecond::Off || (mode == SubSecond::Auto && m % 10 == 0))
swprintf_s(bf, L"-%02d:%02d:%02d", am / timeConstHour, (am / timeConstMinute) % 60, (am / timeConstSecond) % 60);
else
swprintf_s(bf, L"-%02d:%02d:%02d.%d", am / timeConstHour, (am / timeConstMinute) % 60, (am / timeConstSecond) % 60, am % timeConstSecond);
}
else {
if (mode == SubSecond::Off || (mode == SubSecond::Auto && m % 10 == 0))
swprintf_s(bf, L"-%d:%02d:%02d", am / timeConstHour, (am / timeConstMinute) % 60, (am / timeConstSecond) % 60);
else
swprintf_s(bf, L"-%d:%02d:%02d.%d", am / timeConstHour, (am / timeConstMinute) % 60, (am / timeConstSecond) % 60, am % timeConstSecond);
}
}
else { else {
m = 0; m = 0;
bf[0] = 0x2013; bf[0] = 0x2013;
@ -564,15 +665,25 @@ const wstring &getTimeMS(int m) {
return res; return res;
} }
const wstring &formatTime(int rt) { const wstring &formatTime(int rt, SubSecond mode) {
wstring &res = StringCache::getInstance().wget(); wstring &res = StringCache::getInstance().wget();
if (rt>0 && rt<3600*999) { if (rt>0 && rt<timeConstHour*999) {
wchar_t bf[16]; wchar_t bf[40];
if (rt>=3600 && MeOSUtil::useHourFormat) if (mode == SubSecond::Off || (mode == SubSecond::Auto && rt % 10 == 0)) {
swprintf_s(bf, 16, L"%d:%02d:%02d", rt/3600,(rt/60)%60, rt%60); if (rt >= timeConstHour && MeOSUtil::useHourFormat)
else swprintf_s(bf, L"%d:%02d:%02d", rt / timeConstHour, (rt / timeConstMinute) % 60, (rt / timeConstSecond) % 60);
swprintf_s(bf, 16, L"%d:%02d", (rt/60), rt%60); else
swprintf_s(bf, L"%d:%02d", (rt / timeConstMinute), (rt / timeConstSecond) % 60);
}
else {
if (rt >= timeConstHour && MeOSUtil::useHourFormat)
swprintf_s(bf, L"%d:%02d:%02d.%d", rt / timeConstHour, (rt / timeConstMinute) % 60, (rt / timeConstSecond) % 60, rt%timeConstSecond);
else
swprintf_s(bf, L"%d:%02d.%d", (rt / timeConstMinute), (rt / timeConstSecond) % 60, rt%timeConstSecond);
}
res = bf; res = bf;
return res; return res;
} }
@ -583,12 +694,12 @@ const wstring &formatTime(int rt) {
const string &formatTimeN(int rt) { const string &formatTimeN(int rt) {
string &res = StringCache::getInstance().get(); string &res = StringCache::getInstance().get();
if (rt>0 && rt<3600*999) { if (rt>0 && rt<timeConstHour *999) {
char bf[16]; char bf[16];
if (rt>=3600 && MeOSUtil::useHourFormat) if (rt>= timeConstHour && MeOSUtil::useHourFormat)
sprintf_s(bf, 16, "%d:%02d:%02d", rt/3600,(rt/60)%60, rt%60); sprintf_s(bf, 16, "%d:%02d:%02d", rt/ timeConstHour,(rt/timeConstMinute)%60, (rt/timeConstSecond)%60);
else else
sprintf_s(bf, 16, "%d:%02d", (rt/60), rt%60); sprintf_s(bf, 16, "%d:%02d", (rt/timeConstMinute), (rt/timeConstSecond)%60);
res = bf; res = bf;
return res; return res;
@ -597,11 +708,15 @@ const string &formatTimeN(int rt) {
return res; return res;
} }
const wstring &formatTimeHMS(int rt) { const wstring &formatTimeHMS(int rt, SubSecond mode) {
wstring &res = StringCache::getInstance().wget(); wstring &res = StringCache::getInstance().wget();
if (rt>=0) { if (rt>=0) {
wchar_t bf[32]; wchar_t bf[40];
swprintf_s(bf, 16, L"%02d:%02d:%02d", rt/3600,(rt/60)%60, rt%60); if (mode == SubSecond::Off || (mode == SubSecond::Auto && rt%10 == 0))
swprintf_s(bf, 16, L"%02d:%02d:%02d", rt/timeConstHour,(rt/timeConstMinute)%60, (rt/timeConstSecond)%60);
else
swprintf_s(bf, 16, L"%02d:%02d:%02d.%d", rt / timeConstHour, (rt / timeConstMinute) % 60, (rt / timeConstSecond) % 60, rt % timeConstSecond);
res = bf; res = bf;
return res; return res;
} }
@ -612,10 +727,10 @@ const wstring &formatTimeHMS(int rt) {
wstring formatTimeIOF(int rt, int zeroTime) wstring formatTimeIOF(int rt, int zeroTime)
{ {
if (rt>0 && rt<(3600*24*10)) { if (rt > 0) {
rt+=zeroTime; rt += zeroTime;
wchar_t bf[16]; wchar_t bf[16];
swprintf_s(bf, 16, L"%02d:%02d:%02d", (rt/3600)%24,(rt/60)%60, rt%60); swprintf_s(bf, 16, L"%02d:%02d:%02d", (rt / timeConstHour) % 24, (rt / timeConstMinute) % 60, (rt / timeConstSecond) % 60);
return bf; return bf;
} }
@ -766,7 +881,7 @@ wstring itow(int64_t i) {
wstring itow(uint64_t i) { wstring itow(uint64_t i) {
wchar_t bf[32]; wchar_t bf[32];
_i64tow_s(i, bf, 32, 10); _ui64tow_s(i, bf, 32, 10);
return bf; return bf;
} }
@ -819,14 +934,19 @@ bool filterMatchString(const string &c, const char *filt_lc)
return strstr(key, filt_lc)!=0; return strstr(key, filt_lc)!=0;
} }
bool filterMatchString(const wstring &c, const wchar_t *filt_lc) { bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score) {
score = 0;
if (filt_lc[0] == 0) if (filt_lc[0] == 0)
return true; return true;
wchar_t key[2048]; wchar_t key[2048];
wcscpy_s(key, c.c_str()); wcscpy_s(key, c.c_str());
CharLowerBuff(key, c.length()); CharLowerBuff(key, c.length());
bool match = wcsstr(key, filt_lc) != 0;
return wcsstr(key, filt_lc)!=0; if (match) {
while (filt_lc[score] && key[score] && filt_lc[score] == key[score])
score++;
}
return match;
} }
@ -1936,8 +2056,8 @@ int getTimeZoneInfo(const wstring &date) {
else if (datecodeUTC < datecode) else if (datecodeUTC < datecode)
daydiff = -1; daydiff = -1;
int t = st.wHour * 3600; int t = st.wHour * timeConstSecPerHour;
int tUTC = daydiff * 24 * 3600 + utc.wHour * 3600 + utc.wMinute * 60 + utc.wSecond; int tUTC = daydiff * 24 * timeConstSecPerHour + utc.wHour * timeConstSecPerHour + utc.wMinute * timeConstSecPerMin + utc.wSecond;
lastValue = tUTC - t; lastValue = tUTC - t;
return lastValue; return lastValue;
@ -1949,12 +2069,12 @@ wstring getTimeZoneString(const wstring &date) {
return L"+00:00"; return L"+00:00";
else if (a>0) { else if (a>0) {
wchar_t bf[12]; wchar_t bf[12];
swprintf_s(bf, L"-%02d:%02d", a/3600, (a/60)%60); swprintf_s(bf, L"-%02d:%02d", a/timeConstSecPerHour, (a/timeConstMinPerHour)%60);
return bf; return bf;
} }
else { else {
wchar_t bf[12]; wchar_t bf[12];
swprintf_s(bf, L"+%02d:%02d", a/-3600, (a/-60)%60); swprintf_s(bf, L"+%02d:%02d", a/-timeConstSecPerHour, (a/-timeConstMinPerHour)%60);
return bf; return bf;
} }
} }
@ -2291,3 +2411,97 @@ const char* meosException::narrow(const wstring& msg) {
static string nmsg(msg.begin(), msg.end()); static string nmsg(msg.begin(), msg.end());
return nmsg.c_str(); return nmsg.c_str();
} }
int parseRelativeTime(const char *data) {
if (data) {
int ret = atoi(data);
if (timeConstSecond > 1) {
int j = 0;
while (data[j]) {
if (data[j] == '.') {
int t = data[j + 1] - '0';
if (t > 0 && t < 10) {
if (ret < 0 || data[0] == '-')
return ret * timeConstSecond - t;
else
return ret * timeConstSecond + t;
}
break;
}
j++;
}
}
if (ret == -1)
return ret; // Special value
return ret * timeConstSecond;
}
return 0;
}
int parseRelativeTime(const wchar_t *data) {
if (data) {
int ret = _wtoi(data);
if (timeConstSecond > 1) {
int j = 0;
while (data[j]) {
if (data[j] == '.') {
int t = data[j + 1] - '0';
if (t > 0 && t < 10) {
if (ret < 0 || data[0] == '-')
return ret * timeConstSecond - t;
else
return ret * timeConstSecond + t;
}
break;
}
j++;
}
}
if (ret == -1)
return ret; // Special value
return ret * timeConstSecond;
}
return 0;
}
const wstring &codeRelativeTimeW(int rt) {
wchar_t bf[32];
int subSec = timeConstSecond == 1 ? 0 : rt % timeConstSecond;
if (timeConstSecond == 1 || rt == -1)
return itow(rt);
else if (subSec == 0 && rt != -10)
return itow(rt / timeConstSecond);
else if (rt > 0) {
swprintf_s(bf, L"%d.%d", rt / timeConstSecond, rt % timeConstSecond);
}
else {
rt = -rt;
swprintf_s(bf, L"-%d.%d", rt / timeConstSecond, rt % timeConstSecond);
}
wstring &res = StringCache::getInstance().wget();
res = bf;
return res;
}
const string &codeRelativeTime(int rt) {
char bf[32];
int subSec = timeConstSecond == 1 ? 0 : rt % timeConstSecond;
if (timeConstSecond == 1 || rt == -1)
return itos(rt);
else if (subSec == 0 && rt != -10)
return itos(rt / timeConstSecond);
else if (rt > 0) {
sprintf_s(bf, "%d.%d", rt / timeConstSecond, rt % timeConstSecond);
}
else {
rt = -rt;
sprintf_s(bf, "-%d.%d", rt / timeConstSecond, rt % timeConstSecond);
}
string &res = StringCache::getInstance().get();
res = bf;
return res;
}

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -72,9 +72,22 @@ int getRelativeDay();
/// Get time and date in a format that forms a part of a filename /// Get time and date in a format that forms a part of a filename
wstring getLocalTimeFileName(); wstring getLocalTimeFileName();
const wstring &getTimeMS(int m); enum class SubSecond {
const wstring &formatTime(int rt); Off,
const wstring &formatTimeHMS(int rt); On,
Auto
};
int parseRelativeTime(const char *data);
int parseRelativeTime(const wchar_t *data);
const wstring &codeRelativeTimeW(int rt);
const string &codeRelativeTime(int rt);
// Format time MM:SS.t (force2digit=true) or M:SS.t (force2digit=false)
const wstring &formatTimeMS(int m, bool force2digit, SubSecond mode = SubSecond::Auto);
const wstring &formatTime(int rt, SubSecond mode = SubSecond::Auto);
const wstring &formatTimeHMS(int rt, SubSecond mode = SubSecond::Auto);
wstring formatTimeIOF(int rt, int zeroTime); wstring formatTimeIOF(int rt, int zeroTime);
@ -91,8 +104,8 @@ void processGeneralTime(const wstring &generalTime, wstring &meosTime, wstring &
//string formatDate(int m, bool useIsoFormat); //string formatDate(int m, bool useIsoFormat);
wstring formatDate(int m, bool useIsoFormat); wstring formatDate(int m, bool useIsoFormat);
__int64 SystemTimeToInt64Second(const SYSTEMTIME &st); __int64 SystemTimeToInt64TenthSecond(const SYSTEMTIME &st);
SYSTEMTIME Int64SecondToSystemTime(__int64 time); SYSTEMTIME Int64TenthSecondToSystemTime(__int64 time);
#define NOTIME 0x7FFFFFFF #define NOTIME 0x7FFFFFFF
@ -141,7 +154,7 @@ wstring itow(uint64_t i);
///Lower case match (filt_lc must be lc) ///Lower case match (filt_lc must be lc)
bool filterMatchString(const string &c, const char *filt_lc); bool filterMatchString(const string &c, const char *filt_lc);
bool filterMatchString(const wstring &c, const wchar_t *filt_lc); bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score);
bool matchNumber(int number, const wchar_t *key); bool matchNumber(int number, const wchar_t *key);

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -43,3 +43,6 @@ public:
} }
}; };
class meosCancel : public meosException {
};

View File

@ -462,6 +462,7 @@
<ClCompile Include="animationdata.cpp" /> <ClCompile Include="animationdata.cpp" />
<ClCompile Include="autocomplete.cpp" /> <ClCompile Include="autocomplete.cpp" />
<ClCompile Include="autotask.cpp" /> <ClCompile Include="autotask.cpp" />
<ClCompile Include="binencoder.cpp" />
<ClCompile Include="classconfiginfo.cpp" /> <ClCompile Include="classconfiginfo.cpp" />
<ClCompile Include="csvparser.cpp"> <ClCompile Include="csvparser.cpp">
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -669,6 +670,7 @@
<ClInclude Include="autocomplete.h" /> <ClInclude Include="autocomplete.h" />
<ClInclude Include="autocompletehandler.h" /> <ClInclude Include="autocompletehandler.h" />
<ClInclude Include="autotask.h" /> <ClInclude Include="autotask.h" />
<ClInclude Include="binencoder.h" />
<ClInclude Include="classconfiginfo.h" /> <ClInclude Include="classconfiginfo.h" />
<ClInclude Include="gdiconstants.h" /> <ClInclude Include="gdiconstants.h" />
<ClInclude Include="gdifonts.h" /> <ClInclude Include="gdifonts.h" />
@ -710,6 +712,7 @@
<ClInclude Include="speakermonitor.h" /> <ClInclude Include="speakermonitor.h" />
<ClInclude Include="subcommand.h" /> <ClInclude Include="subcommand.h" />
<ClInclude Include="testmeos.h" /> <ClInclude Include="testmeos.h" />
<ClInclude Include="timeconstants.hpp" />
<ClInclude Include="toolbar.h" /> <ClInclude Include="toolbar.h" />
<ClInclude Include="csvparser.h" /> <ClInclude Include="csvparser.h" />
<ClInclude Include="download.h" /> <ClInclude Include="download.h" />

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -23,23 +23,23 @@
#include <vector> #include <vector>
#include "meos_util.h" #include "meos_util.h"
//ABCDEFGHIJKLMNO //ABCDEFGHIJKLMNOP
int getMeosBuild() { int getMeosBuild() {
string revision("$Rev: 1152 $"); string revision("$Rev: 1225 $");
return 174 + atoi(revision.substr(5, string::npos).c_str()); return 174 + atoi(revision.substr(5, string::npos).c_str());
} }
wstring getMeosDate() { wstring getMeosDate() {
wstring date(L"$Date: 2022-05-04 14:47:48 +0200 (ons, 04 maj 2022) $"); wstring date(L"$Date: 2023-02-08 19:30:10 +0100 (ons, 08 feb 2023) $");
return date.substr(7,10); return date.substr(7,10);
} }
wstring getBuildType() { wstring getBuildType() {
return L"U1"; // No parantheses (...) return L"Beta 1"; // No parantheses (...)
} }
wstring getMajorVersion() { wstring getMajorVersion() {
return L"3.8"; return L"3.9";
} }
wstring getMeosFullVersion() { wstring getMeosFullVersion() {
@ -67,31 +67,8 @@ wstring getMeosCompectVersion() {
} }
void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp) void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp)
{ {
developSupp.emplace_back(L"Almby IK, Örebro");//2019-- supp.emplace_back(L"OK Gorm, Denmark");//2020-
supp.emplace_back(L"Ligue PACA");
supp.emplace_back(L"SC vebr-sport");
supp.emplace_back(L"IP Skogen Göteborg");
supp.emplace_back(L"Smedjebackens Orientering");
supp.emplace_back(L"Gudhems IF");
supp.emplace_back(L"Kexholm SK");
supp.emplace_back(L"Utby IK");
supp.emplace_back(L"JWOC 2019");
developSupp.emplace_back(L"OK Nackhe");
supp.emplace_back(L"OK Rodhen");
developSupp.emplace_back(L"SongTao Wang / Henan Zhixing Exploration Sports Culture Co., Ltd.");
developSupp.emplace_back(L"Australian and Oceania Orienteering Championships 2019");
supp.emplace_back(L"Järfälla OK");
supp.emplace_back(L"TJ Slávia Farmaceut Bratislava");
supp.emplace_back(L"Magnus Thornell, Surahammars SOK");
supp.emplace_back(L"Mariager Fjord OK");
supp.emplace_back(L"Nässjö OK");
supp.emplace_back(L"Ringsjö OK");
supp.emplace_back(L"Big Foot Orienteers");
supp.emplace_back(L"Erik Hulthen, Mölndal Outdoor IF");
supp.emplace_back(L"Bay Area Orienteering Club");
supp.emplace_back(L"Finspångs SOK");
supp.emplace_back(L"OK Gorm, Denmark");
supp.emplace_back(L"Nyköpings OK"); supp.emplace_back(L"Nyköpings OK");
supp.emplace_back(L"Thomas Engberg, VK Uvarna"); supp.emplace_back(L"Thomas Engberg, VK Uvarna");
supp.emplace_back(L"LG Axmalm, Sävedalens AIK"); supp.emplace_back(L"LG Axmalm, Sävedalens AIK");
@ -113,11 +90,9 @@ void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp)
supp.emplace_back(L"Fredrik Magnusson, Laholms IF"); supp.emplace_back(L"Fredrik Magnusson, Laholms IF");
supp.emplace_back(L"KOB ATU Košice"); supp.emplace_back(L"KOB ATU Košice");
supp.emplace_back(L"Alfta-Ösa OK"); supp.emplace_back(L"Alfta-Ösa OK");
supp.emplace_back(L"HEYRIES, ACA Aix en Provence");
supp.emplace_back(L"IFK Kiruna"); supp.emplace_back(L"IFK Kiruna");
supp.emplace_back(L"Smedjebackens OK"); supp.emplace_back(L"Smedjebackens OK");
supp.emplace_back(L"Gunnar Persson, Svanesunds GIF"); supp.emplace_back(L"Gunnar Persson, Svanesunds GIF");
supp.emplace_back(L"Kamil Pipek, OK Lokomotiva Pardubice");
supp.emplace_back(L"Køge Orienteringsklub"); supp.emplace_back(L"Køge Orienteringsklub");
supp.emplace_back(L"Simrishamns OK"); supp.emplace_back(L"Simrishamns OK");
supp.emplace_back(L"OK Fryksdalen"); supp.emplace_back(L"OK Fryksdalen");
@ -137,6 +112,7 @@ void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp)
supp.emplace_back(L"Hans Carlstedt, Sävedalens AIK"); supp.emplace_back(L"Hans Carlstedt, Sävedalens AIK");
supp.emplace_back(L"O-Liceo, Spain"); supp.emplace_back(L"O-Liceo, Spain");
developSupp.emplace_back(L"Västerviks OK"); developSupp.emplace_back(L"Västerviks OK");
supp.emplace_back(L"Aarhus 1900 Orientering");
supp.emplace_back(L"Ljusne Ala OK"); supp.emplace_back(L"Ljusne Ala OK");
supp.emplace_back(L"Sävedalens AIK"); supp.emplace_back(L"Sävedalens AIK");
supp.emplace_back(L"Foothills Wanderers Orienteering Club"); supp.emplace_back(L"Foothills Wanderers Orienteering Club");
@ -144,6 +120,22 @@ void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp)
supp.emplace_back(L"Per Ågren, OK Enen"); supp.emplace_back(L"Per Ågren, OK Enen");
supp.emplace_back(L"OK Roslagen"); supp.emplace_back(L"OK Roslagen");
supp.emplace_back(L"OK Kolmården"); supp.emplace_back(L"OK Kolmården");
developSupp.emplace_back(L"Orienteering Queensland Inc.");
supp.emplace_back(L"Eksjö SOK");
supp.emplace_back(L"Kolding OK");
developSupp.emplace_back(L"Alfta-Ösa OK");
supp.emplace_back(L"Erik Almséus, IFK Hedemora OK");
supp.emplace_back(L"IK Gandvik, Skara");
supp.emplace_back(L"Mats Kågeson");
supp.emplace_back(L"Lerums SOK");
supp.emplace_back(L"OSC Hamburg");
supp.emplace_back(L"HEYRIES, ACA Aix en Provence");
developSupp.emplace_back(L"IFK Mora OK");
supp.emplace_back(L"OK Rodhen");
supp.emplace_back(L"Big Foot Orienteers");
developSupp.emplace_back(L"OK Måsen");
supp.emplace_back(L"Ligue PACA");
supp.emplace_back(L"Kamil Pipek, OK Lokomotiva Pardubice");
reverse(supp.begin(), supp.end()); reverse(supp.begin(), supp.end());
} }

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -37,8 +37,11 @@
#include "localizer.h" #include "localizer.h"
#include "gdifonts.h" #include "gdifonts.h"
#include "autocomplete.h" #include "autocomplete.h"
#include "image.h"
#include "binencoder.h"
extern oEvent *gEvent; extern oEvent *gEvent;
extern Image image;
const int MAXLISTPARAMID = 10000000; const int MAXLISTPARAMID = 10000000;
@ -438,9 +441,11 @@ int checksum(const wstring &str) {
return ret; return ret;
} }
void MetaList::initUniqueIndex() const { void MetaList::initUniqueIndex() const {
__int64 ix = 0; int64_t ix = 0;
if (splitPrintInfo)
ix = splitPrintInfo->checkSum();
for (int i = 0; i<4; i++) { for (int i = 0; i<4; i++) {
const vector< vector<MetaListPost> > &lines = data[i]; const vector< vector<MetaListPost> > &lines = data[i];
@ -461,6 +466,15 @@ void MetaList::initUniqueIndex() const {
value = value * 31 + mp.mergeWithPrevious; value = value * 31 + mp.mergeWithPrevious;
value = value * 31 + mp.color; value = value * 31 + mp.color;
value = value * 31 + mp.textAdjust; value = value * 31 + mp.textAdjust;
if (mp.type == lImage) {
value = value * 31 + mp.imageHeight;
value = value * 31 + mp.imageWidth;
value = value * 31 + mp.imageStyle;
value = value * 31 + mp.imageOffsetX;
value = value * 31 + mp.imageOffsetY;
value = value * 31 + mp.imageStyleUnderText;
}
ix = ix * 997 + value; ix = ix * 997 + value;
} }
} }
@ -579,12 +593,23 @@ void MetaList::addRow(int ix) {
static void setFixedWidth(oPrintPost &added, static void setFixedWidth(oPrintPost &added,
const map<tuple<int,int,int>, int> &indexPosToWidth, const map<tuple<int,int,int>, int> &indexPosToWidth,
int type, int j, int k) { int type, int j, int k,
map<tuple<int,int,int>, int>::const_iterator res = indexPosToWidth.find(tuple<int,int,int>(type, j, k)); const MetaListPost &mlp) {
if (res != indexPosToWidth.end()) if (added.type == lImage) {
added.fixedWidth = res->second; added.fixedWidth = mlp.getImageWidth();
else added.fixedHeight = mlp.getImageHeight();
added.fixedWidth = 0; added.dx += mlp.getImageOffsetX();
added.dy += mlp.getImageOffsetY();
if (mlp.imageUnderText())
added.imageNoUpdatePos = true;
}
else {
map<tuple<int, int, int>, int>::const_iterator res = indexPosToWidth.find(tuple<int, int, int>(type, j, k));
if (res != indexPosToWidth.end())
added.fixedWidth = res->second;
else
added.fixedWidth = 0;
}
} }
void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const { void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const {
@ -592,6 +617,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
PositionVer2 pos; PositionVer2 pos;
const bool large = par.useLargeSize; const bool large = par.useLargeSize;
li.lp = par; li.lp = par;
li.setSplitPrintInfo(getSplitPrintInfo());
gdiFonts normal, header, small, italic; gdiFonts normal, header, small, italic;
double s_factor; double s_factor;
oe->calculateResults({}, oEvent::ResultType::ClassResult, false); oe->calculateResults({}, oEvent::ResultType::ClassResult, false);
@ -668,7 +694,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
}; };
for (int i = 0; i<4; i++) { for (int i = 0; i<4; i++) {
const vector< vector<MetaListPost> > &lines = mList.data[i]; const vector<vector<MetaListPost>> &lines = mList.data[i];
gdiFonts defaultFont = normal; gdiFonts defaultFont = normal;
switch (i) { switch (i) {
case 0: case 0:
@ -687,6 +713,10 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
return MetaList::isAllStageType(mp.type) && mp.leg == -1; return MetaList::isAllStageType(mp.type) && mp.leg == -1;
}; };
auto isAllLegType = [](const MetaListPost& mp) {
return MetaList::isAllLegType(mp.type) && mp.leg == -2;
};
for (size_t j = 0; j<lines.size(); j++) { for (size_t j = 0; j<lines.size(); j++) {
if (i == 0 && j == 0) if (i == 0 && j == 0)
defaultFont = boldLarge; defaultFont = boldLarge;
@ -694,8 +724,16 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
dataCopy[i].emplace_back(); dataCopy[i].emplace_back();
vector<MetaListPost> &lineCopy = dataCopy[i].back(); vector<MetaListPost> &lineCopy = dataCopy[i].back();
int firstStage = -1; int firstStage = -1, firstLeg = -1;
for (size_t k = 0; k < lines[j].size(); k++) { for (size_t k = 0; k < lines[j].size(); k++) {
if (lines[j][k].type == lImage) {
auto id = lines[j][k].getImageId();
if (id > 0) {
oe->loadImage(id);
image.reloadImage(id, lines[j][k].getImageStyle() ? Image::ImageMethod::WhiteTransparent : Image::ImageMethod::Default);
}
}
if (isAllStageType(lines[j][k])) { if (isAllStageType(lines[j][k])) {
if (firstStage == -1) if (firstStage == -1)
firstStage = k; firstStage = k;
@ -711,6 +749,26 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
firstStage = -1; firstStage = -1;
} }
} }
if (isAllLegType(lines[j][k])) {
if (firstLeg == -1)
firstLeg = k;
if (k + 1 == lines[j].size() || !isAllLegType(lines[j][k + 1])) {
int ns = 0;
vector<pClass> allC;
oe->getClasses(allC, false);
for (pClass c : allC)
ns = max<int>(ns, c->getNumStages());
for (int s = 1; s <= ns; s++) {
for (size_t f = firstLeg; f <= k; f++) {
lineCopy.push_back(lines[j][f]);
lineCopy.back().leg = s - 1;
}
}
firstLeg = -1;
}
}
else { else {
lineCopy.push_back(lines[j][k]); lineCopy.push_back(lines[j][k]);
} }
@ -786,12 +844,18 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
} }
if (mp.limitWidth && mp.blockWidth > 0) if (mp.limitWidth && mp.blockWidth > 0)
width = gdi.scaleLength(mp.blockWidth); width = gdi.scaleLength(mp.blockWidth);
else else if (mp.type == lImage) {
if (mp.imageUnderText())
width = 1;
else
width = gdi.scaleLength(mp.getImageWidth());
}
else {
width = li.getMaxCharWidth(oe, gdi, par.selection, typeFormats, font, width = li.getMaxCharWidth(oe, gdi, par.selection, typeFormats, font,
oPrintPost::encodeFont(fontFaces[i].font, oPrintPost::encodeFont(fontFaces[i].font,
fontFaces[i].scale).c_str(), fontFaces[i].scale).c_str(),
large, max(mp.blockWidth, extraMinWidth)); large, max(mp.blockWidth, extraMinWidth));
}
++linePostCount[make_pair(i, j)]; // Count how many positions on this line ++linePostCount[make_pair(i, j)]; // Count how many positions on this line
indexPosToWidth[tuple<int,int,int>(i, j, k)] = width; indexPosToWidth[tuple<int,int,int>(i, j, k)] = width;
} }
@ -911,10 +975,10 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
fontFaces[MLHead].scale); fontFaces[MLHead].scale);
added.resultModuleIndex = getResultModuleIndex(oe, li, mp); added.resultModuleIndex = getResultModuleIndex(oe, li, mp);
setFixedWidth(added, indexPosToWidth, MLHead, j, k); setFixedWidth(added, indexPosToWidth, MLHead, j, k, mp);
added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLHead, j, k)]; added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLHead, j, k)];
added.color = mp.color; added.color = mp.color;
if (!mp.mergeWithPrevious) if (!mp.mergeWithPrevious && mp.type != lImage)
base = &added; base = &added;
added.useStrictWidth = mp.getLimitBlockWidth(); added.useStrictWidth = mp.getLimitBlockWidth();
if (added.useStrictWidth) if (added.useStrictWidth)
@ -928,9 +992,14 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
added.fixedWidth = 0; added.fixedWidth = 0;
} }
} }
last = &added; last = mp.type != lImage ? &added : nullptr;
next_dy = max(next_dy, fontHeight[make_pair(font, MLHead)]); if (mp.type == lImage) {
if (!mp.imageUnderText())
next_dy = max(next_dy, gdi.scaleLength(mp.getImageHeight()));
}
else
next_dy = max(next_dy, fontHeight[make_pair(font, MLHead)]);
} }
dy += next_dy; dy += next_dy;
next_dy = lineHeight; next_dy = lineHeight;
@ -965,10 +1034,10 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
fontFaces[MLSubHead].scale); fontFaces[MLSubHead].scale);
added.resultModuleIndex = getResultModuleIndex(oe, li, mp); added.resultModuleIndex = getResultModuleIndex(oe, li, mp);
setFixedWidth(added, indexPosToWidth, MLSubHead, j, k); setFixedWidth(added, indexPosToWidth, MLSubHead, j, k, mp);
added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLSubHead, j, k)]; added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLSubHead, j, k)];
added.color = mp.color; added.color = mp.color;
if (!mp.mergeWithPrevious) if (!mp.mergeWithPrevious && mp.type != lImage)
base = &added; base = &added;
added.useStrictWidth = mp.getLimitBlockWidth(); added.useStrictWidth = mp.getLimitBlockWidth();
@ -983,9 +1052,14 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
added.fixedWidth = 0; added.fixedWidth = 0;
} }
} }
last = &added; last = mp.type != lImage ? &added : nullptr;
next_dy = max(next_dy, fontHeight[make_pair(font, MLSubHead)]); if (mp.type == lImage) {
if (!mp.imageUnderText())
next_dy = max(next_dy, gdi.scaleLength(mp.getImageHeight()));
}
else
next_dy = max(next_dy, fontHeight[make_pair(font, MLSubHead)]);
} }
dy += next_dy; dy += next_dy;
} }
@ -1007,7 +1081,13 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
if (mp.font != formatIgnore) if (mp.font != formatIgnore)
font = mp.font; font = mp.font;
next_dy = max(next_dy, fontHeight[make_pair(font, MLList)]); if (mp.type == lImage) {
if (!mp.imageUnderText())
next_dy = max(next_dy, gdi.scaleLength(mp.getImageHeight()));
}
else
next_dy = max(next_dy, fontHeight[make_pair(font, MLList)]);
bool dmy; bool dmy;
oPrintPost &added = li.addListPost(oPrintPost(mp.type, encode(mp.type, mp.text, dmy), font|mp.textAdjust, oPrintPost &added = li.addListPost(oPrintPost(mp.type, encode(mp.type, mp.text, dmy), font|mp.textAdjust,
pos.get(label, s_factor), pos.get(label, s_factor),
@ -1016,14 +1096,14 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
fontFaces[MLList].scale); fontFaces[MLList].scale);
added.resultModuleIndex = getResultModuleIndex(oe, li, mp); added.resultModuleIndex = getResultModuleIndex(oe, li, mp);
setFixedWidth(added, indexPosToWidth, MLList, j, k); setFixedWidth(added, indexPosToWidth, MLList, j, k, mp);
added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLList, j, k)]; added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLList, j, k)];
added.useStrictWidth = mp.getLimitBlockWidth(); added.useStrictWidth = mp.getLimitBlockWidth();
if (added.useStrictWidth) if (added.useStrictWidth)
added.format |= textLimitEllipsis; added.format |= textLimitEllipsis;
added.color = mp.color; added.color = mp.color;
if (!mp.mergeWithPrevious) if (!mp.mergeWithPrevious && mp.type != lImage)
base = &added; base = &added;
if (last && mp.mergeWithPrevious) { if (last && mp.mergeWithPrevious) {
@ -1033,7 +1113,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
added.fixedWidth = 0; added.fixedWidth = 0;
} }
} }
last = &added; last = mp.type != lImage ? &added : nullptr;
} }
dy += next_dy; dy += next_dy;
} }
@ -1062,7 +1142,13 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
else else
xp = pos.get(label, s_factor); xp = pos.get(label, s_factor);
next_dy = max(next_dy, fontHeight[make_pair(font, MLSubList)]); if (mp.type == lImage) {
if (!mp.imageUnderText())
next_dy = max(next_dy, gdi.scaleLength(mp.getImageHeight()));
}
else
next_dy = max(next_dy, fontHeight[make_pair(font, MLSubList)]);
bool dmy; bool dmy;
oPrintPost &added = li.addSubListPost(oPrintPost(mp.type, encode(mp.type, mp.text, dmy), font|mp.textAdjust, oPrintPost &added = li.addSubListPost(oPrintPost(mp.type, encode(mp.type, mp.text, dmy), font|mp.textAdjust,
xp, dy+sublist_dy, mp.leg == -1 ? parLegNumber : make_pair(mp.leg, true))). xp, dy+sublist_dy, mp.leg == -1 ? parLegNumber : make_pair(mp.leg, true))).
@ -1070,11 +1156,11 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
fontFaces[MLSubList].scale); fontFaces[MLSubList].scale);
added.color = mp.color; added.color = mp.color;
if (!mp.mergeWithPrevious) if (!mp.mergeWithPrevious && mp.type != lImage)
base = &added; base = &added;
added.resultModuleIndex = getResultModuleIndex(oe, li, mp); added.resultModuleIndex = getResultModuleIndex(oe, li, mp);
setFixedWidth(added, indexPosToWidth, MLSubList, j, k); setFixedWidth(added, indexPosToWidth, MLSubList, j, k, mp);
added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLSubList, j, k)]; added.xlimit = indexPosToWidthSrc[tuple<int, int, int>(MLSubList, j, k)];
added.useStrictWidth = mp.getLimitBlockWidth(); added.useStrictWidth = mp.getLimitBlockWidth();
if (added.useStrictWidth) if (added.useStrictWidth)
@ -1087,7 +1173,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par
added.fixedWidth = 0; added.fixedWidth = 0;
} }
} }
last = &added; last = mp.type != lImage ? &added : nullptr;
} }
dy += next_dy; dy += next_dy;
} }
@ -1387,11 +1473,11 @@ bool Position::postAdjust() {
void MetaList::save(const wstring &file, const oEvent *oe) const { void MetaList::save(const wstring &file, const oEvent *oe) const {
xmlparser xml; xmlparser xml;
xml.openOutput(file.c_str(), true); xml.openOutput(file.c_str(), true);
save(xml, oe); save(xml, true, oe);
xml.closeOut(); xml.closeOut();
} }
void MetaList::save(xmlparser &xml, const oEvent *oe) const { void MetaList::save(xmlparser &xml, bool includeImages, const oEvent *oe) const {
initSymbols(); initSymbols();
xml.startTag("MeOSListDefinition", "version", getMajorVersion()); xml.startTag("MeOSListDefinition", "version", getMajorVersion());
// xml.write("Title", defaultTitle); // xml.write("Title", defaultTitle);
@ -1401,6 +1487,13 @@ void MetaList::save(xmlparser &xml, const oEvent *oe) const {
xml.write("ListOrigin", listOrigin); xml.write("ListOrigin", listOrigin);
xml.write("Tag", tag); xml.write("Tag", tag);
xml.write("UID", getUniqueId()); xml.write("UID", getUniqueId());
if (splitPrintInfo) {
xml.startTag("SplitPrint");
splitPrintInfo->serialize(xml);
xml.endTag();
}
xml.write("SortOrder", orderToSymbol[sortOrder]); xml.write("SortOrder", orderToSymbol[sortOrder]);
xml.write("ListType", baseTypeToSymbol[listType]); xml.write("ListType", baseTypeToSymbol[listType]);
xml.write("SubListType", baseTypeToSymbol[listSubType]); xml.write("SubListType", baseTypeToSymbol[listSubType]);
@ -1454,6 +1547,22 @@ void MetaList::save(xmlparser &xml, const oEvent *oe) const {
serialize(xml, "List", getList()); serialize(xml, "List", getList());
serialize(xml, "SubList", getSubList()); serialize(xml, "SubList", getSubList());
if (includeImages) {
set<uint64_t> img;
getUsedImages(img);
Encoder92 binEncoder;
for (auto imgId : img) {
wstring fileName = image.getFileName(imgId);
auto rawData = image.getRawData(imgId);
string encoded;
binEncoder.encode92(rawData, encoded);
vector<pair<string, wstring>> props;
props.emplace_back("filename", fileName);
props.emplace_back("id", itow(imgId));
xml.writeAscii("Image", props, encoded);
}
}
xml.endTag(); xml.endTag();
} }
@ -1475,6 +1584,11 @@ void MetaList::load(const xmlobject &xDef) {
xDef.getObjectString("UID", uniqueIndex); xDef.getObjectString("UID", uniqueIndex);
xDef.getObjectString("ResultModule", resultModule); xDef.getObjectString("ResultModule", resultModule);
auto xSplitPrint = xDef.getObject("SplitPrint");
if (xSplitPrint) {
splitPrintInfo = make_shared<SplitPrintListInfo>();
splitPrintInfo->deserialize(xSplitPrint);
}
// string db = "Specified res mod: " + resultModule + "\n"; // string db = "Specified res mod: " + resultModule + "\n";
// OutputDebugString(db.c_str()); // OutputDebugString(db.c_str());
@ -1523,29 +1637,29 @@ void MetaList::load(const xmlobject &xDef) {
xmlobject xSubListFont = xDef.getObject("SubListFont"); xmlobject xSubListFont = xDef.getObject("SubListFont");
if (xHeadFont) { if (xHeadFont) {
const wchar_t *f = xHeadFont.getw(); const wchar_t *f = xHeadFont.getWPtr();
fontFaces[MLHead].font = f != 0 ? f : L"arial"; fontFaces[MLHead].font = f != nullptr ? f : L"arial";
fontFaces[MLHead].scale = xHeadFont.getObjectInt("scale"); fontFaces[MLHead].scale = xHeadFont.getObjectInt("scale");
fontFaces[MLHead].extraSpaceAbove = xHeadFont.getObjectInt("above"); fontFaces[MLHead].extraSpaceAbove = xHeadFont.getObjectInt("above");
} }
if (xSubHeadFont) { if (xSubHeadFont) {
const wchar_t *f = xSubHeadFont.getw(); const wchar_t *f = xSubHeadFont.getWPtr();
fontFaces[MLSubHead].font = f != 0 ? f : L"arial"; fontFaces[MLSubHead].font = f != nullptr ? f : L"arial";
fontFaces[MLSubHead].scale = xSubHeadFont.getObjectInt("scale"); fontFaces[MLSubHead].scale = xSubHeadFont.getObjectInt("scale");
fontFaces[MLSubHead].extraSpaceAbove = xSubHeadFont.getObjectInt("above"); fontFaces[MLSubHead].extraSpaceAbove = xSubHeadFont.getObjectInt("above");
} }
if (xListFont) { if (xListFont) {
const wchar_t *f = xListFont.getw(); const wchar_t *f = xListFont.getWPtr();
fontFaces[MLList].font = f != 0 ? f : L"arial"; fontFaces[MLList].font = f != nullptr ? f : L"arial";
fontFaces[MLList].scale = xListFont.getObjectInt("scale"); fontFaces[MLList].scale = xListFont.getObjectInt("scale");
fontFaces[MLList].extraSpaceAbove = xListFont.getObjectInt("above"); fontFaces[MLList].extraSpaceAbove = xListFont.getObjectInt("above");
} }
if (xSubListFont) { if (xSubListFont) {
const wchar_t *f = xSubListFont.getw(); const wchar_t *f = xSubListFont.getWPtr();
fontFaces[MLSubList].font = f != 0 ? f : L"arial"; fontFaces[MLSubList].font = f != nullptr ? f : L"arial";
fontFaces[MLSubList].scale = xSubListFont.getObjectInt("scale"); fontFaces[MLSubList].scale = xSubListFont.getObjectInt("scale");
fontFaces[MLSubList].extraSpaceAbove = xSubListFont.getObjectInt("above"); fontFaces[MLSubList].extraSpaceAbove = xSubListFont.getObjectInt("above");
} }
@ -1605,7 +1719,7 @@ void MetaList::load(const xmlobject &xDef) {
xDef.getObjects("Filter", f); xDef.getObjects("Filter", f);
for (size_t k = 0; k<f.size(); k++) { for (size_t k = 0; k<f.size(); k++) {
string attrib = f[k].getAttrib("name").get(); string attrib = f[k].getAttrib("name").getStr();
if (symbolToFilter.count(attrib) == 0) { if (symbolToFilter.count(attrib) == 0) {
string err = "Invalid filter X#" + attrib; string err = "Invalid filter X#" + attrib;
throw std::exception(err.c_str()); throw std::exception(err.c_str());
@ -1620,13 +1734,29 @@ void MetaList::load(const xmlobject &xDef) {
xDef.getObjects("SubFilter", f); xDef.getObjects("SubFilter", f);
for (size_t k = 0; k<f.size(); k++) { for (size_t k = 0; k<f.size(); k++) {
string attrib = f[k].getAttrib("name").get(); string attrib = f[k].getAttrib("name").getStr();
if (symbolToSubFilter.count(attrib) == 0) { if (symbolToSubFilter.count(attrib) == 0) {
string err = "Invalid filter X#" + attrib; string err = "Invalid filter X#" + attrib;
throw std::exception(err.c_str()); throw std::exception(err.c_str());
} }
addSubFilter(symbolToSubFilter[attrib]); addSubFilter(symbolToSubFilter[attrib]);
} }
xmlList imgs;
xDef.getObjects("Image", imgs);
Encoder92 binEncoder;
vector<uint8_t> bytes;
for (auto& img : imgs) {
wstring fileName, id;
img.getObjectString("filename", fileName);
img.getObjectString("id", id);
uint64_t imgId = _wcstoui64(id.c_str(), nullptr, 10);
string data = img.getRawStr();
binEncoder.decode92(data, bytes);
image.provideFromMemory(imgId, fileName, bytes);
}
} }
void MetaList::getDynamicResults(vector<DynamicResultRef> &resultModules) const { void MetaList::getDynamicResults(vector<DynamicResultRef> &resultModules) const {
@ -1729,6 +1859,14 @@ bool MetaListContainer::updateResultModule(const DynamicResult &dr, bool updateS
return changed; return changed;
} }
void MetaListContainer::getUsedImages(set<uint64_t>& imgId) const {
for (size_t i = 0; i < data.size(); i++) {
if (data[i].first == ExternalList) {
data[i].second.getUsedImages(imgId);
}
}
}
void MetaList::serialize(xmlparser &xml, const string &tagp, void MetaList::serialize(xmlparser &xml, const string &tagp,
const vector< vector<MetaListPost> > &lp) const { const vector< vector<MetaListPost> > &lp) const {
xml.startTag(tagp.c_str()); xml.startTag(tagp.c_str());
@ -1802,6 +1940,8 @@ void MetaListPost::getTypes(vector< pair<wstring, size_t> > &types, int &current
continue; continue;
if (it->first == lAlignNext) if (it->first == lAlignNext)
continue; continue;
if (it->first == lImage)
continue;
types.push_back(make_pair(lang.tl(it->second), it->first)); types.push_back(make_pair(lang.tl(it->second), it->first));
} }
@ -1895,6 +2035,21 @@ void MetaListPost::serialize(xmlparser &xml) const {
xml.write("PackPrevious", packPrevious); xml.write("PackPrevious", packPrevious);
xml.write("LimitWidth", limitWidth); xml.write("LimitWidth", limitWidth);
if (imageWidth != 0)
xml.write("ImageWidth", imageWidth);
if (imageHeight != 0)
xml.write("ImageHeight", imageHeight);
if (imageOffsetX != 0)
xml.write("ImageOffsetX", imageOffsetX);
if (imageOffsetY != 0)
xml.write("ImageOffsetY", imageOffsetY);
if (imageStyle != 0)
xml.write("ImageStyle", imageStyle);
if (imageStyleUnderText)
xml.writeBool("UnderText", imageStyleUnderText);
if (font != formatIgnore) if (font != formatIgnore)
xml.write("Font", getFont()); xml.write("Font", getFont());
@ -1914,7 +2069,7 @@ void MetaListPost::deserialize(const xmlobject &xml) {
if (!xml) if (!xml)
throw meosException("Ogiltigt filformat"); throw meosException("Ogiltigt filformat");
wstring tp = xml.getAttrib("Type").wget(); wstring tp = xml.getAttrib("Type").getWStr();
if (MetaList::symbolToType.count(tp) == 0) { if (MetaList::symbolToType.count(tp) == 0) {
wstring err = L"Invalid type X#" + tp; wstring err = L"Invalid type X#" + tp;
throw meosException(err); throw meosException(err);
@ -1946,6 +2101,15 @@ void MetaListPost::deserialize(const xmlobject &xml) {
limitWidth = xml.getObjectBool("LimitWidth"); limitWidth = xml.getObjectBool("LimitWidth");
mergeWithPrevious = xml.getObjectInt("MergePrevious") != 0; mergeWithPrevious = xml.getObjectInt("MergePrevious") != 0;
imageWidth = xml.getObjectInt("ImageWidth");
imageHeight = xml.getObjectInt("ImageHeight");
imageOffsetX = xml.getObjectInt("ImageOffsetX");
imageOffsetY = xml.getObjectInt("ImageOffsetY");
imageStyle = xml.getObjectInt("ImageStyle");
imageStyleUnderText = xml.getObjectBool("UnderText");
xml.getObjectString("TextAdjust", at); xml.getObjectString("TextAdjust", at);
if (at == L"Right") if (at == L"Right")
@ -1970,6 +2134,28 @@ void MetaListPost::deserialize(const xmlobject &xml) {
} }
} }
void MetaList::getUsedImages(set<uint64_t>& imgId) const {
for (auto& v1 : data) {
for (auto& v2 : v1) {
for (auto& v3 : v2) {
if (v3.type == lImage) {
auto id = v3.getImageId();
if (id != 0)
imgId.insert(id);
}
}
}
}
}
uint64_t MetaListPost::getImageId() const {
if (type == lImage) {
uint64_t imgId = _wcstoui64(getText().c_str(), nullptr, 10);
return imgId;
}
throw meosException("Internal error");
}
map<EPostType, wstring> MetaList::typeToSymbol; map<EPostType, wstring> MetaList::typeToSymbol;
map<wstring, EPostType> MetaList::symbolToType; map<wstring, EPostType> MetaList::symbolToType;
map<oListInfo::EBaseType, string> MetaList::baseTypeToSymbol; map<oListInfo::EBaseType, string> MetaList::baseTypeToSymbol;
@ -2006,6 +2192,7 @@ void MetaList::initSymbols() {
typeToSymbol[lClassNumEntries] = L"ClassNumEntries"; typeToSymbol[lClassNumEntries] = L"ClassNumEntries";
typeToSymbol[lCourseLength] = L"CourseLength"; typeToSymbol[lCourseLength] = L"CourseLength";
typeToSymbol[lCourseName] = L"CourseName"; typeToSymbol[lCourseName] = L"CourseName";
typeToSymbol[lCourseNumber] = L"CourseNumber";
typeToSymbol[lCourseClimb] = L"CourseClimb"; typeToSymbol[lCourseClimb] = L"CourseClimb";
typeToSymbol[lCourseUsage] = L"CourseUsage"; typeToSymbol[lCourseUsage] = L"CourseUsage";
typeToSymbol[lCourseUsageNoVacant] = L"CourseUsageNoVacant"; typeToSymbol[lCourseUsageNoVacant] = L"CourseUsageNoVacant";
@ -2066,6 +2253,7 @@ void MetaList::initSymbols() {
typeToSymbol[lResultModuleNumberTeam] = L"ResultModuleNumberTeam"; typeToSymbol[lResultModuleNumberTeam] = L"ResultModuleNumberTeam";
typeToSymbol[lRunnerBirthYear] = L"RunnerBirthYear"; typeToSymbol[lRunnerBirthYear] = L"RunnerBirthYear";
typeToSymbol[lRunnerBirthDate] = L"RunnerBirthDate";
typeToSymbol[lRunnerAge] = L"RunnerAge"; typeToSymbol[lRunnerAge] = L"RunnerAge";
typeToSymbol[lRunnerSex] = L"RunnerSex"; typeToSymbol[lRunnerSex] = L"RunnerSex";
typeToSymbol[lRunnerNationality] = L"RunnerNationality"; typeToSymbol[lRunnerNationality] = L"RunnerNationality";
@ -2080,6 +2268,10 @@ void MetaList::initSymbols() {
typeToSymbol[lTeamName] = L"TeamName"; typeToSymbol[lTeamName] = L"TeamName";
typeToSymbol[lTeamStart] = L"TeamStart"; typeToSymbol[lTeamStart] = L"TeamStart";
typeToSymbol[lTeamCourseName] = L"TeamCourseName";
typeToSymbol[lTeamCourseNumber] = L"TeamCourseNumber";
typeToSymbol[lTeamLegName] = L"TeamLegName";
typeToSymbol[lTeamStartCond] = L"TeamStartCond"; typeToSymbol[lTeamStartCond] = L"TeamStartCond";
typeToSymbol[lTeamStartZero] = L"TeamStartZero"; typeToSymbol[lTeamStartZero] = L"TeamStartZero";
@ -2160,7 +2352,13 @@ void MetaList::initSymbols() {
typeToSymbol[lControlRunnersLeft] = L"ControlRunnersLeft"; typeToSymbol[lControlRunnersLeft] = L"ControlRunnersLeft";
typeToSymbol[lControlCodes] = L"ControlCodes"; typeToSymbol[lControlCodes] = L"ControlCodes";
typeToSymbol[lNumEntries] = L"NumEntries";
typeToSymbol[lNumStarts] = L"NumStarts";
typeToSymbol[lTotalRunLength] = L"TotalRunLength";
typeToSymbol[lTotalRunTime] = L"TotalRunTime";
typeToSymbol[lLineBreak] = L"LineBreak"; typeToSymbol[lLineBreak] = L"LineBreak";
typeToSymbol[lImage] = L"Image";
for (map<EPostType, wstring>::iterator it = typeToSymbol.begin(); for (map<EPostType, wstring>::iterator it = typeToSymbol.begin();
it != typeToSymbol.end(); ++it) { it != typeToSymbol.end(); ++it) {
@ -2175,7 +2373,8 @@ void MetaList::initSymbols() {
baseTypeToSymbol[oListInfo::EBaseTypeRunner] = "Runner"; baseTypeToSymbol[oListInfo::EBaseTypeRunner] = "Runner";
baseTypeToSymbol[oListInfo::EBaseTypeTeam] = "Team"; baseTypeToSymbol[oListInfo::EBaseTypeTeam] = "Team";
baseTypeToSymbol[oListInfo::EBaseTypeClub] = "ClubRunner"; baseTypeToSymbol[oListInfo::EBaseTypeClubRunner] = "ClubRunner";
baseTypeToSymbol[oListInfo::EBaseTypeClubTeam] = "ClubTeam";
baseTypeToSymbol[oListInfo::EBaseTypeCoursePunches] = "CoursePunches"; baseTypeToSymbol[oListInfo::EBaseTypeCoursePunches] = "CoursePunches";
baseTypeToSymbol[oListInfo::EBaseTypeAllPunches] = "AllPunches"; baseTypeToSymbol[oListInfo::EBaseTypeAllPunches] = "AllPunches";
baseTypeToSymbol[oListInfo::EBaseTypeNone] = "None"; baseTypeToSymbol[oListInfo::EBaseTypeNone] = "None";
@ -2199,6 +2398,7 @@ void MetaList::initSymbols() {
orderToSymbol[ClassStartTime] = "ClassStartTime"; orderToSymbol[ClassStartTime] = "ClassStartTime";
orderToSymbol[ClassStartTimeClub] = "ClassStartTimeClub"; orderToSymbol[ClassStartTimeClub] = "ClassStartTimeClub";
orderToSymbol[ClubClassStartTime] = "ClubClassStartTime";
orderToSymbol[ClassResult] = "ClassResult"; orderToSymbol[ClassResult] = "ClassResult";
orderToSymbol[ClassDefaultResult] = "ClassDefaultResult"; orderToSymbol[ClassDefaultResult] = "ClassDefaultResult";
orderToSymbol[ClassCourseResult] = "ClassCourseResult"; orderToSymbol[ClassCourseResult] = "ClassCourseResult";
@ -2323,6 +2523,15 @@ MetaList &MetaListContainer::getList(int index) {
return data[index].second; return data[index].second;
} }
const MetaList& MetaListContainer::getList(EStdListType type) const {
auto res = globalIndex.find(type);
if (res != globalIndex.end())
return getList(res->second);
throw meosException("List Error: X#Unknown type " + itos(type));
}
MetaList &MetaListContainer::addExternal(const MetaList &ml) { MetaList &MetaListContainer::addExternal(const MetaList &ml) {
data.push_back(make_pair(ExternalList, ml)); data.push_back(make_pair(ExternalList, ml));
if (owner) if (owner)
@ -2343,7 +2552,7 @@ void MetaListContainer::save(MetaListType type, xmlparser &xml, const oEvent *oe
for (size_t k = 0; k<data.size(); k++) { for (size_t k = 0; k<data.size(); k++) {
if (data[k].first == type) if (data[k].first == type)
data[k].second.save(xml, oe); data[k].second.save(xml, false, oe);
} }
if (type == ExternalList) { if (type == ExternalList) {
setupIndex(EFirstLoadedList); setupIndex(EFirstLoadedList);
@ -2385,7 +2594,7 @@ bool MetaListContainer::load(MetaListType type, const xmlobject &xDef, bool igno
xmlattrib ver = xList[k].getAttrib("version"); xmlattrib ver = xList[k].getAttrib("version");
bool newVersion = false; bool newVersion = false;
if (ver) { if (ver) {
wstring vers = ver.wget(); wstring vers = ver.getWStr();
if (vers > majVer) { if (vers > majVer) {
newVersion = true; newVersion = true;
} }
@ -2451,6 +2660,7 @@ bool MetaListContainer::load(MetaListType type, const xmlobject &xDef, bool igno
it->second.nextList = 0; // Clear relation it->second.nextList = 0; // Clear relation
} }
} }
if (!pushLevel) { if (!pushLevel) {
xDef.getObjects("MeOSResultCalculationSet", xList); xDef.getObjects("MeOSResultCalculationSet", xList);
decltype(freeResultModules) copy; decltype(freeResultModules) copy;
@ -2467,7 +2677,7 @@ bool MetaListContainer::load(MetaListType type, const xmlobject &xDef, bool igno
freeResultModules.emplace(dr->getTag(), GeneralResultCtr(dr->getTag().c_str(), dr->getName(false), dr)); freeResultModules.emplace(dr->getTag(), GeneralResultCtr(dr->getTag().c_str(), dr->getName(false), dr));
} }
if (owner) if (owner)
owner->updateChanged(); owner->updateChanged();
} }
@ -2661,7 +2871,7 @@ EStdListType MetaListContainer::getType(const int index) const {
} }
void MetaListContainer::getLists(vector<pair<wstring, size_t> > &lists, bool markBuiltIn, void MetaListContainer::getLists(vector<pair<wstring, size_t> > &lists, bool markBuiltIn,
bool resultListOnly, bool noTeamList) const { bool resultListOnly, bool noTeamList, bool onlyForSplitPrint) const {
lists.clear(); lists.clear();
size_t currentIx = 0; size_t currentIx = 0;
for (bool i : {false, true}) { for (bool i : {false, true}) {
@ -2677,6 +2887,9 @@ void MetaListContainer::getLists(vector<pair<wstring, size_t> > &lists, bool mar
if ((data[k].first == InternalList) == (i != markBuiltIn)) if ((data[k].first == InternalList) == (i != markBuiltIn))
continue; continue;
if (onlyForSplitPrint && !data[k].second.isSplitPrintList())
continue;
if (data[k].first == InternalList) { if (data[k].first == InternalList) {
if (markBuiltIn) if (markBuiltIn)
lists.push_back(make_pair(L"[" + lang.tl(data[k].second.getListName()) + L"]", k)); lists.push_back(make_pair(L"[" + lang.tl(data[k].second.getListName()) + L"]", k));
@ -2790,11 +3003,15 @@ void MetaListContainer::saveList(int index, const MetaList &ml) {
if (data[index].first == InternalList) if (data[index].first == InternalList)
throw meosException("Invalid list type"); throw meosException("Invalid list type");
string oldId = data[index].second.getUniqueId();
data[index].first = ExternalList; data[index].first = ExternalList;
data[index].second = ml; data[index].second = ml;
data[index].second.initUniqueIndex(); data[index].second.initUniqueIndex();
if (owner) if (owner) {
owner->updateListReferences(oldId, data[index].second.getUniqueId());
owner->updateChanged(); owner->updateChanged();
}
} }
void MetaList::getFilters(vector< pair<wstring, bool> > &filters) const { void MetaList::getFilters(vector< pair<wstring, bool> > &filters) const {
@ -2894,6 +3111,7 @@ void MetaList::getSortOrder(bool forceIncludeCustom, vector< pair<wstring, size_
if (it->first != Custom || forceIncludeCustom || !resultModule.empty() || currentOrder == Custom) if (it->first != Custom || forceIncludeCustom || !resultModule.empty() || currentOrder == Custom)
orders.push_back(make_pair(lang.tl(it->second), it->first)); orders.push_back(make_pair(lang.tl(it->second), it->first));
} }
sort(orders.begin(), orders.end());
currentOrder = sortOrder; currentOrder = sortOrder;
} }
@ -2906,7 +3124,7 @@ void MetaList::getBaseType(vector< pair<wstring, size_t> > &types, int &currentT
continue; continue;
types.push_back(make_pair(lang.tl(it->second), it->first)); types.push_back(make_pair(lang.tl(it->second), it->first));
} }
sort(types.begin(), types.end());
currentType = listType; currentType = listType;
} }
@ -2937,6 +3155,7 @@ void MetaList::getSubType(vector< pair<wstring, size_t> > &types, int &currentTy
types.push_back(make_pair(lang.tl(baseTypeToSymbol[t]), t)); types.push_back(make_pair(lang.tl(baseTypeToSymbol[t]), t));
} }
sort(types.begin()+1, types.end());
currentType = listSubType; currentType = listSubType;
} }
@ -3098,7 +3317,8 @@ EPostType MetaList::getTypeFromSymbol(wstring &symb) noexcept {
void MetaList::fillSymbols(vector < pair<wstring, size_t>> &symb) { void MetaList::fillSymbols(vector < pair<wstring, size_t>> &symb) {
for (auto s : symbolToType) { for (auto s : symbolToType) {
if (s.second == lAlignNext || s.second == lNone || s.second == lLineBreak) if (s.second == lAlignNext || s.second == lNone
|| s.second == lLineBreak || s.second == lImage)
continue; continue;
wstring desc = L"[" + s.first + L"]\t" + lang.tl(s.first); wstring desc = L"[" + s.first + L"]\t" + lang.tl(s.first);
symb.emplace_back(desc, size_t(s.second)); symb.emplace_back(desc, size_t(s.second));
@ -3224,19 +3444,44 @@ void MetaListContainer::updateGeneralResult(string tag, const shared_ptr<Dynamic
owner->updateChanged(); owner->updateChanged();
} }
void MetaList::getAutoComplete(const wstring &w, vector<AutoCompleteRecord> &records) { void MetaList::getAutoComplete(const wstring& w, vector<AutoCompleteRecord>& records) {
records.clear(); records.clear();
wchar_t s_lc[1024]; vector<wstring> ws, ws2;
wcscpy_s(s_lc, w.c_str()); split(w, L" ", ws);
CharLowerBuff(s_lc, w.length());
vector<vector<wchar_t>> s_lc(ws.size());
for (int j = 0; j < ws.size(); j++) {
s_lc[j].resize(ws[j].size() + 1);
ws[j] = trim(ws[j]);
wcscpy_s(s_lc[j].data(), s_lc[j].size(), ws[j].c_str());
CharLowerBuff(s_lc[j].data(), ws[j].length());
}
wstring tl; wstring tl;
for (auto &ts : typeToSymbol) { for (auto& ts : typeToSymbol) {
if (ts.first == lNone || ts.first == lAlignNext) if (ts.first == lNone || ts.first == lAlignNext || ts.first == lImage)
continue; continue;
tl = lang.tl(ts.second); tl = lang.tl(ts.second);
if (filterMatchString(ts.second, s_lc) || filterMatchString(tl, s_lc)) { int score = 0;
records.emplace_back(tl, ts.second, ts.first); int fScore;
for (int j = 0; j < ws.size(); j++) {
if (filterMatchString(ts.second, s_lc[j].data(), fScore))
score += 1 + fScore * (ws.size() == 1) ? 2 : 0;
if (filterMatchString(tl, s_lc[j].data(), fScore)) {
score++;
split(tl, L" ", ws2);
for (int i = 0; i < ws2.size(); i++) {
if (filterMatchString(ws2[i], s_lc[j].data(), fScore)) {
int diff = std::abs(int(ws2[i].length() - ws[j].length()));
score += max(5 - diff, 0) + fScore;
}
}
}
} }
if (score > 0)
records.emplace_back(tl, score, ts.second, ts.first);
} }
} }

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -94,6 +94,15 @@ private:
gdiFonts font; gdiFonts font;
int textAdjust; // 0, textRight, textCenter int textAdjust; // 0, textRight, textCenter
GDICOLOR color; GDICOLOR color;
// Image
int imageWidth = 0;
int imageHeight = 0;
int imageStyle = 0;
int imageOffsetX = 0;
int imageOffsetY = 0;
bool imageStyleUnderText = false;
public: public:
MetaListPost(EPostType type_, EPostType align_ = lNone, int leg_ = -1); MetaListPost(EPostType type_, EPostType align_ = lNone, int leg_ = -1);
@ -109,11 +118,47 @@ public:
MetaListPost &limitBlockWidth(bool lim = true) { limitWidth = lim; return *this; } MetaListPost &limitBlockWidth(bool lim = true) { limitWidth = lim; return *this; }
MetaListPost &packWithPrevious(bool pack = true) { packPrevious = pack; return *this; } MetaListPost &packWithPrevious(bool pack = true) { packPrevious = pack; return *this; }
MetaListPost &indent(int ind) {minimalIndent = ind; return *this;} MetaListPost &indent(int ind) {minimalIndent = ind; return *this;}
void getTypes(vector< pair<wstring, size_t> > &types, int &currentType) const; void getTypes(vector< pair<wstring, size_t> > &types, int &currentType) const;
uint64_t getImageId() const;
void setImageDimension(int x, int y) {
imageWidth = x;
imageHeight = y;
}
int getImageWidth() const {
return imageWidth;
}
int getImageHeight() const {
return imageHeight;
}
void setImageOffset(int x, int y) {
imageOffsetX = x;
imageOffsetY = y;
}
int getImageOffsetX() const {
return imageOffsetX;
}
int getImageOffsetY() const {
return imageOffsetY;
}
int getImageStyle() const {
return imageStyle;
}
void setImageStyle(int style) {
imageStyle = style;
}
bool imageUnderText() const {
return imageStyleUnderText;
}
void imageUnderText(bool ut) {
imageStyleUnderText = ut;
}
const wstring &getType() const; const wstring &getType() const;
MetaListPost &setType(EPostType type_) {type = type_; return *this;} MetaListPost &setType(EPostType type_) {type = type_; return *this;}
EPostType getTypeRaw() const { return type; } EPostType getTypeRaw() const { return type; }
@ -236,8 +281,20 @@ private:
static bool isBreak(int x); static bool isBreak(int x);
shared_ptr<SplitPrintListInfo> splitPrintInfo;
public: public:
bool isSplitPrintList() const { return splitPrintInfo != nullptr; }
const shared_ptr<SplitPrintListInfo>& getSplitPrintInfo() const {
return splitPrintInfo;
}
void setSplitPrintInfo(const shared_ptr<SplitPrintListInfo>& info) {
splitPrintInfo = info;
}
static wstring encode(EPostType type, const wstring &input, bool &foundSymbol); static wstring encode(EPostType type, const wstring &input, bool &foundSymbol);
static const wstring &fromResultModuleNumber(const wstring &in, int nr, wstring &out); static const wstring &fromResultModuleNumber(const wstring &in, int nr, wstring &out);
@ -246,12 +303,21 @@ public:
MetaList(); MetaList();
virtual ~MetaList() {} virtual ~MetaList() {}
void getUsedImages(set<uint64_t>& imgId) const;
static constexpr bool isAllStageType(EPostType type) { static constexpr bool isAllStageType(EPostType type) {
return type == lRunnerStagePlace || type == lRunnerStageStatus || return type == lRunnerStagePlace || type == lRunnerStageStatus ||
type == lRunnerStageTime || type == lRunnerStageTimeStatus || type == lRunnerStageTime || type == lRunnerStageTimeStatus ||
type == lRunnerStagePoints || type == lRunnerStageNumber; type == lRunnerStagePoints || type == lRunnerStageNumber;
} }
static constexpr bool isAllLegType(EPostType type) {
return type == lTeamCourseName || type == lTeamCourseNumber ||
type == lTeamLegName || type == lTeamRunner || type == lTeamRunnerCard ||
type == lTeamLegTimeStatus || type == lTeamLegTimeAfter ||
type == lTeamPlace || type ==lTeamStart;
}
static constexpr bool isResultModuleOutput(EPostType type) { static constexpr bool isResultModuleOutput(EPostType type) {
return type == lResultModuleNumber || type == lResultModuleTime || return type == lResultModuleNumber || type == lResultModuleTime ||
type == lResultModuleTimeTeam || type == lResultModuleNumberTeam; type == lResultModuleTimeTeam || type == lResultModuleNumberTeam;
@ -329,7 +395,7 @@ public:
bool isValidIx(size_t gIx, size_t lIx, size_t ix) const; bool isValidIx(size_t gIx, size_t lIx, size_t ix) const;
void save(xmlparser &xml, const oEvent *oe) const; void save(xmlparser &xml, bool includeImages, const oEvent *oe) const;
void load(const xmlobject &xDef); void load(const xmlobject &xDef);
void interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const; void interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const;
@ -371,6 +437,8 @@ public:
friend class MetaListPost; friend class MetaListPost;
}; };
class Image;
class MetaListContainer { class MetaListContainer {
public: public:
enum MetaListType {InternalList, ExternalList, RemovedList}; enum MetaListType {InternalList, ExternalList, RemovedList};
@ -391,7 +459,6 @@ public:
virtual ~MetaListContainer(); virtual ~MetaListContainer();
void getFreeResultModules(vector<pair<string, shared_ptr<DynamicResult>>> &res) const; void getFreeResultModules(vector<pair<string, shared_ptr<DynamicResult>>> &res) const;
string getUniqueId(EStdListType code) const; string getUniqueId(EStdListType code) const;
@ -400,6 +467,8 @@ public:
bool updateResultModule(const DynamicResult &res, bool updateSimilar); bool updateResultModule(const DynamicResult &res, bool updateSimilar);
void getUsedImages(set<uint64_t>& imgId) const;
int getNumParam() const {return listParam.size();} int getNumParam() const {return listParam.size();}
int getNumLists() const {return data.size();} int getNumLists() const {return data.size();}
int getNumLists(MetaListType) const; int getNumLists(MetaListType) const;
@ -409,6 +478,7 @@ public:
const MetaList &getList(int index) const; const MetaList &getList(int index) const;
MetaList &getList(int index); MetaList &getList(int index);
const MetaList& getList(EStdListType type) const;
const oListParam &getParam(int index) const; const oListParam &getParam(int index) const;
oListParam &getParam(int index); oListParam &getParam(int index);
@ -421,7 +491,8 @@ public:
void getLists(vector< pair<wstring, size_t> > &lists, void getLists(vector< pair<wstring, size_t> > &lists,
bool markBuiltIn, bool markBuiltIn,
bool resultListOnly, bool resultListOnly,
bool noTeamList) const; bool noTeamList,
bool onlyForSplitPrint) const;
const string &getTag(int index) const; const string &getTag(int index) const;

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -563,14 +563,14 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) {
w[i] = gdi.scaleLength(w[i]); w[i] = gdi.scaleLength(w[i]);
set<wstring> errors; set<wstring> errors;
currentResult->prepareCalculations(*oe, false, {}, rr, tr, inputNumber); currentResult->prepareCalculations(*oe, true, {}, rr, tr, inputNumber);
for (size_t k = 0; k < rr.size(); k++) { for (size_t k = 0; k < rr.size(); k++) {
int txp = xp; int txp = xp;
int wi = 0; int wi = 0;
gdi.addStringUT(yp, txp, 0, rr[k]->getCompleteIdentification(), w[wi]-diff); gdi.addStringUT(yp, txp, 0, rr[k]->getCompleteIdentification(), w[wi]-diff);
txp += w[wi++]; txp += w[wi++];
currentResult->prepareCalculations(*rr[k], false); currentResult->prepareCalculations(*rr[k], true);
int rt = 0, pt = 0; int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown; RunnerStatus st = StatusUnknown;
@ -674,7 +674,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) {
int wi = 0; int wi = 0;
gdi.addStringUT(yp, txp, 0, tr[k]->getName(), w[wi]-diff); gdi.addStringUT(yp, txp, 0, tr[k]->getName(), w[wi]-diff);
txp += w[wi++]; txp += w[wi++];
currentResult->prepareCalculations(*tr[k], false); currentResult->prepareCalculations(*tr[k], true);
int rt = 0, pt = 0; int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown; RunnerStatus st = StatusUnknown;
{ {
@ -920,10 +920,10 @@ void MethodEditor::makeDirty(gdioutput &gdi, DirtyFlag inside) {
bool MethodEditor::checkSave(gdioutput &gdi) { bool MethodEditor::checkSave(gdioutput &gdi) {
if (dirtyInt) { if (dirtyInt) {
gdioutput::AskAnswer answer = gdi.askCancel(L"Vill du spara ändringar?"); gdioutput::AskAnswer answer = gdi.askCancel(L"Vill du spara ändringar?");
if (answer == gdioutput::AnswerCancel) if (answer == gdioutput::AskAnswer::AnswerCancel)
return false; return false;
if (answer == gdioutput::AnswerYes) { if (answer == gdioutput::AskAnswer::AnswerYes) {
gdi.sendCtrlMessage("SaveInside"); gdi.sendCtrlMessage("SaveInside");
} }
makeDirty(gdi, ClearDirty); makeDirty(gdi, ClearDirty);
@ -1004,11 +1004,11 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) {
if (isTeam) if (isTeam)
oe->getTeams(art->getClassId(true), tt, false); oe->getTeams(art->getClassId(true), tt, false);
currentResult->prepareCalculations(*oe, false, {art->getClassId(true)}, rr, tt, inputNumber); currentResult->prepareCalculations(*oe, true, {art->getClassId(true)}, rr, tt, inputNumber);
gdi_new->addString("", fontMediumPlus, "Debug Output"); gdi_new->addString("", fontMediumPlus, "Debug Output");
if (!isTeam) { if (!isTeam) {
oRunner &r = *pRunner(art); oRunner &r = *pRunner(art);
currentResult->prepareCalculations(r, false); currentResult->prepareCalculations(r, true);
int rt = 0, pt = 0; int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown; RunnerStatus st = StatusUnknown;
gdi.dropLine(); gdi.dropLine();
@ -1065,7 +1065,7 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) {
} }
else { else {
oTeam &t = *pTeam(art); oTeam &t = *pTeam(art);
currentResult->prepareCalculations(t, false); currentResult->prepareCalculations(t, true);
int rt = 0, pt = 0; int rt = 0, pt = 0;
RunnerStatus st = StatusUnknown; RunnerStatus st = StatusUnknown;
gdi.dropLine(); gdi.dropLine();

View File

@ -2,7 +2,7 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/************************************************************************ /************************************************************************
MeOS - Orienteering Software MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -29,7 +29,7 @@
#include "MeosSQL.h" #include "MeosSQL.h"
#include <process.h> #include <process.h>
MySQLReconnect::MySQLReconnect(const wstring &errorIn) : AutoMachine("MySQL-daemon", Machines::mMySQLReconnect), error(errorIn) { MySQLReconnect::MySQLReconnect(const wstring &errorIn) : AutoMachine("MySQL-service", Machines::mMySQLReconnect), error(errorIn) {
timeError = getLocalTime(); timeError = getLocalTime();
hThread=0; hThread=0;
} }
@ -43,7 +43,7 @@ bool MySQLReconnect::stop() {
if (interval==0) if (interval==0)
return true; return true;
return MessageBox(0, L"If this daemon is stopped, then MeOS will not reconnect to the network. Continue?", return MessageBox(0, L"If this service is stopped, MeOS will not reconnect to the network. Continue?",
L"Warning", MB_YESNO|MB_ICONWARNING)==IDYES; L"Warning", MB_YESNO|MB_ICONWARNING)==IDYES;
} }

Some files were not shown because too many files have changed in this diff Show More