sizeSet(k+1, false);
+
+ fout << "\n";
+
+ int linecounter=0;
+ it=TL.begin();
+
+ vector< pair > > rows;
+ rows.reserve(TL.size() / 3);
+ vector ypRow;
+ int minHeight = 100000;
+
+ while (it!=TL.end()) {
+ int y=it->yp;
+ vector row;
+
+ int subnormal = 0;
+ int normal = 0;
+ int header = 0;
+ int mainheader = 0;
+ while (it!=TL.end() && it->yp==y) {
+ if (!gdioutput::skipTextRender(it->format)) {
+ row.push_back(&*it);
+ switch (it->getGdiFont()) {
+ case fontLarge:
+ case boldLarge:
+ case boldHuge:
+ mainheader++;
+ break;
+ case boldText:
+ case italicMediumPlus:
+ case fontMediumPlus:
+ header++;
+ break;
+ case fontSmall:
+ case italicSmall:
+ subnormal++;
+ break;
+ default:
+ normal++;
+ }
+ }
+ ++it;
+ }
+
+ if (row.empty())
+ continue;
+
+ bool isMainHeader = mainheader > normal;
+ bool isHeader = (header + mainheader) > normal;
+ bool isSub = subnormal > normal;
+
+ sort(row.begin(), row.end(), sortTL_X);
+ rows.resize(rows.size() + 1);
+ rows.back().first = isMainHeader ? 1 : (isHeader ? 2 : (isSub ? 3 : 0));
+ rows.back().second.swap(row);
+ int last = ypRow.size();
+ ypRow.push_back(y);
+ if (last > 0) {
+ minHeight = min(minHeight, ypRow[last] - ypRow[last-1]);
+ }
+ }
+ int numMin = 0;
+ for (size_t gCount = 1; gCount < rows.size(); gCount++) {
+ int h = ypRow[gCount] - ypRow[gCount-1];
+ if (h == minHeight)
+ numMin++;
+ }
+ if (numMin == 0)
+ numMin = 1;
+
+ int hdrLimit = (rows.size() / numMin) <= 4 ? int(minHeight * 1.2) : int(minHeight * 1.5);
+ for (size_t gCount = 1; gCount + 1 < rows.size(); gCount++) {
+ int type = rows[gCount].first;
+ int lastType = gCount > 0 ? 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))
+ continue; // No reclassify
+
+ int h = ypRow[gCount] - ypRow[gCount-1];
+ if (h > hdrLimit && rows[gCount].first == 0)
+ rows[gCount].first = 2;
+ }
+
+ ypRow.clear();
+ string lineclass;
+ for (size_t gCount = 0; gCount < rows.size(); gCount++) {
+ vector &row = rows[gCount].second;
+ int type = rows[gCount].first;
+ int lastType = gCount > 0 ? rows[gCount-1].first : 0;
+ int nextType = gCount + 1 < rows.size() ? rows[gCount + 1].first : 0;
+
+ vector::iterator rit;
+ fout << "" << endl;
+
+ if (simpleFormat) {
+ }
+ else if (type == 1) {
+ lineclass = " class=\"freeheader\"";
+ linecounter = 0;
+ }
+ else if (type == 2) {
+ linecounter = 0;
+ lineclass = " valign=\"bottom\" class=\"header\"";
+ }
+ else {
+ if (type == 3)
+ linecounter = 1;
+
+ if ((lastType == 1 || lastType == 2) && (nextType == 1 || nextType == 2) && row.size() < 3) {
+ lineclass = "";
+ }
+ else
+ lineclass = (linecounter&1) ? " class=\"e1\"" : " class=\"e0\"";
+
+ linecounter++;
+ }
+
+ for (size_t k=0;kxp];
+
+ if (k==0 && thisCol!=0)
+ fout << "| | ";
+
+ int nextCol;
+ if (row.size()==k+1)
+ nextCol=tableCoordinates.rbegin()->second;
+ else
+ nextCol=tableCoordinates[row[k+1]->xp];
+
+ int colspan=nextCol-thisCol;
+
+ assert(colspan>0);
+
+ string style;
+
+ if (row[k]->format&textRight)
+ style=" style=\"text-align:right\"";
+
+ if (colspan==1 && !sizeSet[thisCol]) {
+ fout << " xp - row[k]->xp) : (MaxX-row[k]->xp)) << "\">";
+ sizeSet[thisCol]=true;
+ }
+ else if (colspan>1)
+ fout << " | ";
+ else
+ fout << " | ";
+
+ gdiFonts font = row[k]->getGdiFont();
+ string starttag, endtag;
+ getStyle(styles, font, row[k]->font, "", starttag, endtag);
+
+ fout << starttag << toUTF8(html_table_code(row[k]->text)) << endtag << " | " << endl;
+
+ }
+ fout << "
\n";
+
+ row.clear();
+ }
+
+ fout << "
\n";
+
+ if (!simpleFormat) {
+ fout << "
";
+ char bf1[256];
+ char bf2[256];
+ GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf2, 256);
+ GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, bf1, 256);
+ string meos = getMeosCompectVersion();
+ fout << toUTF8(lang.tl("Skapad av ")) + "MeOS "
+ << meos << ": " << bf1 << " "<< bf2 << "\n";
+ fout << "
\n";
+ }
+ fout << "\n";
+ fout << "\n";
+
+ return true;
+}
\ No newline at end of file
diff --git a/code/MeOSFeatures.cpp b/code/MeOSFeatures.cpp
new file mode 100644
index 0000000..eeb5b0a
--- /dev/null
+++ b/code/MeOSFeatures.cpp
@@ -0,0 +1,251 @@
+/************************************************************************
+ MeOS - Orienteering Software
+ Copyright (C) 2009-2017 Melin Software HB
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ Melin Software HB - software@melin.nu - www.melin.nu
+ Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
+
+************************************************************************/
+
+#include "stdafx.h"
+#include "oEvent.h"
+#include "MeOSFeatures.h"
+#include "meosexception.h"
+#include "meos_util.h"
+#include
+#include "classconfiginfo.h"
+
+MeOSFeatures::MeOSFeatures(void)
+{
+ addHead("General");
+ add(DrawStartList, "SL", "Prepare start lists");
+ add(Bib, "BB", "Bibs");
+ add(Clubs, "CL", "Clubs");
+ add(EditClub, "CC", "Edit Clubs").require(Clubs);
+
+ add(InForest, "RF", "Track runners in forest");
+ add(Network, "NW", "Several MeOS Clients in a network");
+
+ addHead("MeOS Features");
+ add(Speaker, "SP", "Använd speakerstöd");
+ add(SeveralStages, "ST", "Several stages");
+ add(Economy, "EC", "Economy and fees").require(EditClub).require(Clubs);
+ add(Vacancy, "VA", "Vacancies and entry cancellations").require(DrawStartList);
+ add(TimeAdjust, "TA", "Manual time penalties and adjustments");
+ add(RunnerDb, "RD", "Club and runner database").require(Clubs);
+ addHead("Teams and forking");
+ add(ForkedIndividual, "FO", "Forked individual courses");
+ add(Patrol, "PT", "Patrols");
+ add(Relay, "RL", "Relays");
+ add(MultipleRaces, "MR", "Several races for a runner").require(Relay);
+
+ addHead("Rogaining");
+ add(Rogaining, "RO", "Rogaining");
+ add(PointAdjust, "PA", "Manual point reductions and adjustments").require(Rogaining);
+}
+
+MeOSFeatures::FeatureDescriptor &MeOSFeatures::add(Feature feat, const char *code, const char *descr) {
+ assert(codeToIx.count(code) == 0);
+ assert(featureToIx.count(feat) == 0);
+
+ featureToIx[feat] = desc.size();
+ string codeS = code;
+ codeToIx[codeS] = desc.size();
+ desc.push_back(FeatureDescriptor(feat, codeS, descr));
+ return desc.back();
+}
+
+MeOSFeatures::FeatureDescriptor &MeOSFeatures::addHead(const char *descr) {
+ desc.push_back(FeatureDescriptor(_Head, "-", descr));
+ return desc.back();
+}
+
+bool MeOSFeatures::isHead(int featureIx) const {
+ return getFeature(featureIx) == _Head;
+}
+
+const string &MeOSFeatures::getHead(int featureIx) const {
+ if (isHead(featureIx))
+ return desc[featureIx].desc;
+ else
+ isHead(-1);//Throws
+ return _EmptyString;
+}
+
+MeOSFeatures::FeatureDescriptor::FeatureDescriptor(Feature featIn,
+ string codeIn,
+ string descIn) :
+ feat(featIn), code(codeIn), desc(descIn)
+{
+}
+
+MeOSFeatures::~MeOSFeatures(void)
+{
+}
+
+bool MeOSFeatures::hasFeature(Feature f) const {
+ return features.count(f) != 0;
+}
+
+void MeOSFeatures::useFeature(Feature f, bool use, oEvent &oe) {
+ if (use) {
+ const set &dep = desc[getIndex(f)].dependsOn;
+ features.insert(f);
+ features.insert(dep.begin(), dep.end());
+ }
+ else {
+ if (!isRequiredInternal(f))
+ features.erase(f);
+ }
+ oe.getDI().setString("Features", serialize());
+}
+
+bool MeOSFeatures::isRequiredInternal(Feature f) const {
+ for (set::const_iterator it = features.begin(); it != features.end(); ++it) {
+ if (desc[getIndex(*it)].dependsOn.count(f))
+ return true;
+ }
+ return false;
+}
+
+bool MeOSFeatures::isRequired(Feature f, const oEvent &oe) const {
+ if (isRequiredInternal(f))
+ return true;
+
+ if (f == Rogaining && oe.hasRogaining() && hasFeature(Rogaining))
+ return true;
+
+ return false;
+}
+
+int MeOSFeatures::getNumFeatures() const {
+ return desc.size();
+}
+
+MeOSFeatures::Feature MeOSFeatures::getFeature(int featureIx) const {
+ if (size_t(featureIx) < desc.size())
+ return desc[featureIx].feat;
+ else
+ throw meosException("Index out of bounds");
+}
+
+const string &MeOSFeatures::getDescription(Feature f) const {
+ return desc[getIndex(f)].desc;
+}
+
+const string &MeOSFeatures::getCode(Feature f) const {
+ return desc[getIndex(f)].code;
+}
+
+int MeOSFeatures::getIndex(Feature f) const {
+ map::const_iterator res = featureToIx.find(f);
+ if (res == featureToIx.end())
+ throw meosException("Index out of bounds");
+ return res->second;
+}
+
+string MeOSFeatures::serialize() const {
+ if (features.empty())
+ return "NONE";
+
+ string st;
+ for (set::const_iterator it = features.begin(); it != features.end(); ++it) {
+ if (!st.empty())
+ st += "+";
+ st += getCode(*it);
+ }
+ return st;
+}
+
+void MeOSFeatures::deserialize(const string &input, oEvent &oe) {
+ features.clear();
+
+ if (input == "NONE")
+ return;
+ else if (input.empty()) {
+ loadDefaults(oe);
+ }
+
+ vector ff;
+ split(input, "+", ff);
+ for (size_t k = 0; k < ff.size(); k++) {
+ map::iterator res = codeToIx.find(ff[k]);
+ if (res != codeToIx.end())
+ features.insert(desc[res->second].feat);
+ }
+
+ set iF;
+ for (set::iterator it = features.begin(); it != features.end(); ++it) {
+ int ix = getIndex(*it);
+ iF.insert(desc[ix].dependsOn.begin(), desc[ix].dependsOn.end());
+ }
+
+ features.insert(iF.begin(), iF.end());
+}
+
+void MeOSFeatures::loadDefaults(oEvent &oe) {
+ if (oe.getDCI().getInt("UseEconomy") != 0) {
+ features.insert(Economy);
+ features.insert(EditClub);
+ }
+
+ if (oe.getDCI().getInt("UseSpeaker") != 0)
+ features.insert(Speaker);
+
+ if (oe.hasRogaining())
+ features.insert(Rogaining);
+
+ if (oe.getDCI().getInt("SkipRunnerDb") == 0 )
+ features.insert(RunnerDb);
+
+ ClassConfigInfo cnf;
+ oe.getClassConfigurationInfo(cnf);
+ if (cnf.hasPatrol())
+ features.insert(Patrol);
+
+ if (cnf.hasRelay())
+ features.insert(Relay);
+
+ if (cnf.raceNStart.size() > 0) {
+ features.insert(Relay);
+ features.insert(MultipleRaces);
+ }
+
+ if (cnf.isMultiStageEvent())
+ features.insert(SeveralStages);
+
+ features.insert(Clubs);
+ features.insert(Network);
+ features.insert(ForkedIndividual);
+
+ features.insert(Vacancy);
+ features.insert(InForest);
+ features.insert(DrawStartList);
+ features.insert(Bib);
+}
+
+void MeOSFeatures::useAll(oEvent &oe) {
+ for (size_t k = 0; k < desc.size(); k++) {
+ if (desc[k].feat != _Head)
+ features.insert(desc[k].feat);
+ }
+ oe.getDI().setString("Features", serialize());
+}
+
+void MeOSFeatures::clear(oEvent &oe) {
+ features.clear();
+ oe.getDI().setString("Features", serialize());
+}
diff --git a/code/MeOSFeatures.h b/code/MeOSFeatures.h
new file mode 100644
index 0000000..6b03a08
--- /dev/null
+++ b/code/MeOSFeatures.h
@@ -0,0 +1,103 @@
+#pragma once
+/************************************************************************
+ MeOS - Orienteering Software
+ Copyright (C) 2009-2017 Melin Software HB
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ Melin Software HB - software@melin.nu - www.melin.nu
+ Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
+
+************************************************************************/
+
+#include
+#include
+#include