Added reload of levels on F7 (Update levelpack) to ease the test of changes.
[enigmagame.git] / src / StateManager.cc
blob95efc0a078e5bef9fbca9829374e4c06f6d033bb
1 /*
2 * Copyright (C) 2006 Ronald Lamprecht
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "StateManager.hh"
21 #include "errors.hh"
22 #include "main.hh"
23 #include "DOMErrorReporter.hh"
24 #include "DOMSchemaResolver.hh"
25 #include "LocalToXML.hh"
26 #include "nls.hh"
27 #include "options.hh"
28 #include "Utf8ToXML.hh"
29 #include "utilXML.hh"
30 #include "XMLtoLocal.hh"
31 #include "XMLtoUtf8.hh"
32 #include "ecl_system.hh"
33 #include "gui/ErrorMenu.hh"
34 #include <cstdio>
35 #include <iostream>
36 #include <sstream>
37 #include <xercesc/dom/DOM.hpp>
38 #include <xercesc/util/XMLDouble.hpp>
39 #include <xercesc/util/XMLString.hpp>
40 #include <xercesc/util/XMLUniDefs.hpp>
41 #include <xercesc/util/PlatformUtils.hpp>
42 #include <xercesc/util/XercesVersion.hpp>
43 #if _XERCES_VERSION < 30000
44 #include <xercesc/framework/LocalFileFormatTarget.hpp>
45 #endif
48 using namespace std;
49 using namespace enigma;
50 XERCES_CPP_NAMESPACE_USE
52 namespace enigma {
53 StateManager *StateManager::theSingleton = 0;
55 StateManager* StateManager::instance() {
56 if (theSingleton == 0) {
57 theSingleton = new StateManager();
59 return theSingleton;
62 StateManager::StateManager() {
63 std::string statePath;
64 std::string errMessage;
66 if (!app.resourceFS->findFile( "state.xml" , statePath)) {
67 if (!app.systemFS->findFile( "schemas/state.xml" , statePath)) {
68 throw XFrontend("Cannot load application state template!");
72 try {
73 std::ostringstream errStream;
74 app.domParserErrorHandler->resetErrors();
75 app.domParserErrorHandler->reportToOstream(&errStream);
76 app.domParserSchemaResolver->resetResolver();
77 app.domParserSchemaResolver->addSchemaId("state.xsd","state.xsd");
79 doc = app.domParser->parseURI(statePath.c_str());
80 if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) {
81 propertiesElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
82 Utf8ToXML("properties").x_str())->item(0));
83 groupsElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
84 Utf8ToXML("groups").x_str())->item(0));
85 groupList = groupsElem->getElementsByTagName(
86 Utf8ToXML("group").x_str());
87 indicesElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
88 Utf8ToXML("indices").x_str())->item(0));
89 indexList = indicesElem->getElementsByTagName(
90 Utf8ToXML("index").x_str());
91 levelsElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
92 Utf8ToXML("levels").x_str())->item(0));
94 if(app.domParserErrorHandler->getSawErrors()) {
95 errMessage = errStream.str();
97 app.domParserErrorHandler->reportToNull(); // do not report to errStream any more
99 catch (...) {
100 errMessage = "Unexpected XML Exception on load of state\n";
102 if (!errMessage.empty()) {
103 throw XFrontend("Cannot load application state file: " + statePath +
104 "\nError: " + errMessage);
108 StateManager::~StateManager() {
109 if (doc != NULL)
110 shutdown();
113 bool StateManager::save() {
114 bool result = true;
115 std::string errMessage;
117 if (doc == NULL)
118 return true;
120 int count = getInt("Count");
121 setProperty("Count", ++count);
123 stripIgnorableWhitespace(doc->getDocumentElement());
124 std::string path = app.userPath + "/state.xml";
125 std::string pathBackup = app.userPath + "/backup/state.xml";
127 // backup state every 10th save
128 if (count%10 == 0) {
129 std::remove((pathBackup + "~2").c_str());
130 std::remove((path + "~2").c_str()); // 1.00 bakups
131 if (ecl::FileExists(path + "~1")) {
132 if (std::difftime(ecl::FileModTime(path + "~1"),
133 ecl::FileModTime(pathBackup + "~1")) > 0) {
134 // backup 1 from 1.00 is newer than backup 1 on backup path
135 if (Copyfile(path + "~1", pathBackup + "~2"))
136 std::remove((path + "~1").c_str()); // 1.00 bakup
137 } else {
138 // just in case off previous copy failure
139 std::rename((pathBackup + "~1").c_str(), (pathBackup + "~2").c_str());
140 std::remove((path + "~1").c_str()); // 1.00 bakup
142 } else {
143 std::rename((pathBackup + "~1").c_str(), (pathBackup + "~2").c_str());
145 Copyfile(path, pathBackup + "~1");
148 try {
149 #if _XERCES_VERSION >= 30000
150 result = app.domSer->writeToURI(doc, LocalToXML(& path).x_str());
151 #else
152 XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(path.c_str());
153 result = app.domSer->writeNode(myFormTarget, *doc);
154 delete myFormTarget; // flush
155 #endif
156 } catch (const XMLException& toCatch) {
157 errMessage = std::string("Exception on save of state: \n") +
158 XMLtoUtf8(toCatch.getMessage()).c_str() + "\n";
159 result = false;
160 } catch (const DOMException& toCatch) {
161 errMessage = std::string("Exception on save of state: \n") +
162 XMLtoUtf8(toCatch.getMessage()).c_str() + "\n";
163 result = false;
164 } catch (...) {
165 errMessage = "Unexpected exception on save of state\n" ;
166 result = false;
169 if (!result) {
170 if (count%10 == 0) {
171 // restore backup in case of error
172 if (Copyfile(pathBackup + "~1", path)) {
173 std::remove((pathBackup + "~1").c_str());
174 std::rename((pathBackup + "~2").c_str(), (pathBackup + "~1").c_str());
177 cerr << XMLtoLocal(Utf8ToXML(errMessage.c_str()).x_str()).c_str();
178 gui::ErrorMenu m(errMessage, N_("Continue"));
179 m.manage();
180 } else
181 Log << "State save o.k.\n";
183 return result;
186 void StateManager::shutdown() {
187 save();
188 if (doc != NULL)
189 doc->release();
190 doc = NULL;
193 void StateManager::getGroupNames(std::vector<std::string> *names) {
194 for (int i = 0, l = groupList-> getLength(); i < l; i++) {
195 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
196 names->push_back(XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str());
200 std::string StateManager::getGroupSelectedIndex(std::string groupName) {
201 for (int i = 0, l = groupList-> getLength(); i < l; i++) {
202 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
203 if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str())
204 return XMLtoUtf8(group->getAttribute(Utf8ToXML("curindex").x_str())).c_str();
206 return "";
209 std::string StateManager::getGroupSelectedColumn(std::string groupName) {
210 for (int i = 0, l = groupList-> getLength(); i < l; i++) {
211 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
212 if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str())
213 return XMLtoUtf8(group->getAttribute(Utf8ToXML("curcolumn").x_str())).c_str();
215 return 0;
218 void StateManager::setGroupSelectedIndex(std::string groupName, std::string indexName) {
219 for (int i = 0, l = groupList-> getLength(); i < l; i++) {
220 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
221 if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
222 group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
223 return;
228 void StateManager::setGroupSelectedColumn(std::string groupName, std::string column) {
229 for (int i = 0, l = groupList-> getLength(); i < l; i++) {
230 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
231 if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
232 group->setAttribute(Utf8ToXML("curcolumn").x_str(), Utf8ToXML(column).x_str());
233 return;
238 void StateManager::addGroup(std::string groupName, std::string indexName, int column) {
239 // check if group exists - update attributes only
240 for (int i = 0, l = groupList->getLength(); i < l; i++) {
241 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
242 if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
243 group->setAttribute(Utf8ToXML("curcolumn").x_str(),
244 Utf8ToXML(ecl::strf("%d",column)).x_str());
245 group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
246 return;
249 // if group does not exist add a new element
250 DOMElement * group = doc->createElement (Utf8ToXML("group").x_str());
251 group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(groupName).x_str());
252 group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
253 group->setAttribute(Utf8ToXML("curcolumn").x_str(),
254 Utf8ToXML(ecl::strf("%d",column)).x_str());
255 groupsElem->appendChild(group);
258 void StateManager::insertGroup(int pos, std::string groupName,
259 std::string indexName, std::string column) {
260 DOMElement * group = doc->createElement (Utf8ToXML("group").x_str());
261 group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(groupName).x_str());
262 group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
263 group->setAttribute(Utf8ToXML("curcolumn").x_str(), Utf8ToXML(column).x_str());
264 if (pos < 0 || pos >= groupList->getLength())
265 groupsElem->appendChild(group);
266 else {
267 DOMElement * nextGroup = dynamic_cast<DOMElement *>(groupList->item(pos));
268 groupsElem->insertBefore(group, nextGroup);
272 void StateManager::deleteGroup(std::string groupName) {
273 for (int i = 0, l = groupList->getLength(); i < l; i++) {
274 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
275 if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
276 groupsElem->removeChild(group);
277 return;
282 void StateManager::renameGroup(std::string oldName, std::string newName) {
283 // rename group element
284 for (int i = 0, l = groupList->getLength(); i < l; i++) {
285 DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
286 if (oldName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
287 group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(newName).x_str());
288 break;
294 void StateManager::addIndex(std::string indexName, std::string &groupName,
295 double &location, int &curpos, int &curfirst) {
296 // check if index exists - get user attributes
297 for (int i = 0, l = indexList-> getLength(); i < l; i++) {
298 DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
299 if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
300 groupName = XMLtoUtf8(index->getAttribute(Utf8ToXML("group").x_str())).c_str();
301 XMLDouble * result = new XMLDouble(index->getAttribute(Utf8ToXML("location").x_str()));
302 location = result->getValue();
303 delete result;
304 curpos = XMLString::parseInt(index->getAttribute(Utf8ToXML("curposition").x_str()));
305 curfirst = XMLString::parseInt(index->getAttribute(Utf8ToXML("curfirst").x_str()));
306 return;
309 // if index does not exist add a new element with default values
310 DOMElement * index = doc->createElement (Utf8ToXML("index").x_str());
311 index->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(indexName).x_str());
312 index->setAttribute(Utf8ToXML("group").x_str(), Utf8ToXML(groupName).x_str());
313 index->setAttribute(Utf8ToXML("location").x_str(), Utf8ToXML(ecl::strf("%g",location)).x_str());
314 index->setAttribute(Utf8ToXML("curfirst").x_str(), Utf8ToXML(ecl::strf("%d",0)).x_str());
315 index->setAttribute(Utf8ToXML("curposition").x_str(), Utf8ToXML(ecl::strf("%d",0)).x_str());
316 indicesElem->appendChild(index);
319 void StateManager::setIndexName(std::string oldName, std::string newName) {
320 // search index and set attribute
321 for (int i = 0, l = indexList-> getLength(); i < l; i++) {
322 DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
323 if (oldName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
324 index->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(newName).x_str());
325 return;
330 void StateManager::setIndexLocation(std::string indexName, double location) {
331 // search index and set attribute
332 for (int i = 0, l = indexList-> getLength(); i < l; i++) {
333 DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
334 if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
335 index->setAttribute(Utf8ToXML("location").x_str(), Utf8ToXML(ecl::strf("%.15g",location)).x_str());
336 return;
341 void StateManager::setIndexCurpos(std::string indexName, int curpos) {
342 // search index and set attribute
343 for (int i = 0, l = indexList-> getLength(); i < l; i++) {
344 DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
345 if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
346 index->setAttribute(Utf8ToXML("curposition").x_str(), Utf8ToXML(ecl::strf("%d",curpos)).x_str());
347 return;
352 void StateManager::setIndexCurfirst(std::string indexName, int curfirst) {
353 // search index and set attribute
354 for (int i = 0, l = indexList-> getLength(); i < l; i++) {
355 DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
356 if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
357 index->setAttribute(Utf8ToXML("curfirst").x_str(), Utf8ToXML(ecl::strf("%d",curfirst)).x_str());
358 return;
363 void StateManager::setIndexGroup(std::string indexName, std::string groupName) {
364 // search index and set attribute
365 for (int i = 0, l = indexList-> getLength(); i < l; i++) {
366 DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
367 if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
368 index->setAttribute(Utf8ToXML("group").x_str(), Utf8ToXML(groupName).x_str());
369 return;
375 std::string StateManager::getAnnotation(std::string id) {
376 DOMElement * level = getLevel(id);
377 if (level != NULL)
378 return XMLtoUtf8(level->getAttribute(Utf8ToXML("annotation").x_str())).c_str();
379 else
380 return "";
383 void StateManager::setAnnotation(std::string id, std::string annotation) {
384 DOMElement * level = getLevel(id);
385 if (level == NULL) {
386 level = doc->createElement (Utf8ToXML("level").x_str());
387 level->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(id).x_str());
388 levelsElem->appendChild(level);
390 level->setAttribute(Utf8ToXML("annotation").x_str(), Utf8ToXML(annotation).x_str());
393 DOMElement * StateManager::getLevel(std::string id) {
394 XMLCh * xmlId = XMLString::replicate(Utf8ToXML(id).x_str());
395 bool levelFound = false;
396 DOMElement * level;
397 DOMNodeList * levelList = levelsElem->getElementsByTagName(Utf8ToXML("level").x_str());
398 for (int i = 0, l = levelList-> getLength(); i < l && !levelFound; i++) {
399 level = dynamic_cast<DOMElement *>(levelList->item(i));
400 if (XMLString::equals(xmlId,
401 level->getAttribute(Utf8ToXML("id").x_str()))) {
402 levelFound = true;
405 XMLString::release(&xmlId);
406 return levelFound ? level : NULL;
408 } // namespace enigma