File indexing completed on 2024-04-28 04:04:48
0001 /*************************************************************************** 0002 * Copyright 2007 Francesco Rossi <redsh@email.it> * 0003 * Copyright 2006-2007 Mick Kappenburg <ksudoku@kappendburg.net> * 0004 * Copyright 2006-2007 Johannes Bergmeier <johannes.bergmeier@gmx.net> * 0005 * Copyright 2015 Ian Wadham <iandw.au@gmail.com> * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify * 0008 * it under the terms of the GNU General Public License as published by * 0009 * the Free Software Foundation; either version 2 of the License, or * 0010 * (at your option) any later version. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, * 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0015 * GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License * 0018 * along with this program; if not, write to the * 0019 * Free Software Foundation, Inc., * 0020 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0021 ***************************************************************************/ 0022 0023 #include "serializer.h" 0024 #include "ksudokugame.h" 0025 #include "puzzle.h" 0026 0027 #include <QDomDocument> 0028 #include <QFile> 0029 #include <QTextStream> 0030 #include <QTemporaryFile> 0031 0032 #include <KIO/FileCopyJob> 0033 #include <KIO/StoredTransferJob> 0034 #include <KJobWidgets> 0035 #include <KLocalizedString> 0036 0037 #include "ksudoku.h" 0038 #include "symbols.h" 0039 #include "settings.h" 0040 0041 namespace ksudoku { 0042 0043 const char * typeNames[] = {"Plain", "XSudoku", "Jigsaw", "Aztec", 0044 "Samurai", "TinySamurai", "Roxdoku", 0045 "Mathdoku", "KillerSudoku"}; 0046 const SudokuType types[] = {Plain, XSudoku, Jigsaw, Aztec, 0047 Samurai, TinySamurai, Roxdoku, 0048 Mathdoku, KillerSudoku}; 0049 0050 Game Serializer::deserializeGame(const QDomElement &element) { 0051 bool hasPuzzle = false; 0052 Puzzle* puzzle = nullptr; 0053 bool hasHistory = false; 0054 QList<HistoryEvent> history; 0055 0056 bool hadHelp = static_cast<bool>(element.attribute(QStringLiteral("had-help"), QStringLiteral("0")).toInt()); 0057 int msecsElapsed = element.attribute(QStringLiteral("msecs-elapsed"), QStringLiteral("0")).toInt(); 0058 0059 QDomNode child = element.firstChild(); 0060 while (!child.isNull()) { 0061 if(child.isElement()) { 0062 if(child.nodeName() == QLatin1String("puzzle")) { 0063 if(hasPuzzle) { 0064 delete puzzle; 0065 return Game(); 0066 } 0067 0068 puzzle = deserializePuzzle(child.toElement()); 0069 hasPuzzle = true; 0070 } else if(child.nodeName() == QLatin1String("history")) { 0071 if(hasHistory) { 0072 delete puzzle; 0073 return Game(); 0074 } 0075 0076 history = deserializeHistory(child.toElement()); 0077 hasHistory = true; 0078 } 0079 } 0080 child = child.nextSibling(); 0081 } 0082 0083 if(!puzzle) return Game(); 0084 0085 Game game(puzzle); 0086 game.setUserHadHelp(hadHelp); 0087 0088 if(hasHistory) { 0089 for(int i = 0; i < history.count(); ++i) 0090 game.doEvent(history[i]); 0091 } 0092 0093 game.setTime(msecsElapsed); 0094 return game; 0095 } 0096 0097 Puzzle* Serializer::deserializePuzzle(const QDomElement &element) { 0098 bool hasGraph = false; 0099 SKGraph* graph = nullptr; 0100 bool hasValues = false; 0101 QString valuesStr; 0102 bool hasSolution = false; 0103 QString solutionStr; 0104 0105 QString content; 0106 QDomNode child = element.firstChild(); 0107 while (!child.isNull()) { 0108 if(child.isElement()) { 0109 if(child.nodeName() == QLatin1String("graph")) { 0110 if(hasGraph) { 0111 delete graph; 0112 return nullptr; 0113 } 0114 0115 graph = deserializeGraph(child.toElement()); 0116 hasGraph = true; 0117 } else if(child.nodeName() == QLatin1String("values")) { 0118 if(hasValues) { 0119 delete graph; 0120 return nullptr; 0121 } 0122 0123 valuesStr = child.toElement().text(); 0124 hasValues = true; 0125 } else if(child.nodeName() == QLatin1String("solution")) { 0126 // TODO remove deserialization of solution, it is no longer required 0127 if(hasSolution) { 0128 delete graph; 0129 return nullptr; 0130 } 0131 0132 solutionStr = child.toElement().text(); 0133 hasSolution = true; 0134 } 0135 } 0136 child = child.nextSibling(); 0137 } 0138 0139 if(!graph) return nullptr; 0140 if(valuesStr.length() != graph->size()) { 0141 delete graph; 0142 return nullptr; 0143 } 0144 // TODO remove deserialization of solution, it is no longer required 0145 if(solutionStr.length() != 0 && solutionStr.length() != graph->size()) { 0146 delete graph; 0147 return nullptr; 0148 } 0149 0150 auto* puzzle = new Puzzle(graph, hasSolution); 0151 0152 BoardContents values; 0153 values.resize(graph->size()); 0154 for(int i = 0; i < graph->size(); ++i) { 0155 values[i] = Symbols::ioSymbol2Value(valuesStr[i]); 0156 } 0157 0158 // TODO remove deserialization of solution, it is no longer required 0159 BoardContents solution; 0160 if(solutionStr.length() != 0) { 0161 solution.resize(graph->size()); 0162 for(int i = 0; i < graph->size(); ++i) { 0163 solution[i] = Symbols::ioSymbol2Value(solutionStr[i]); 0164 } 0165 } 0166 0167 puzzle->init(values); 0168 return puzzle; 0169 } 0170 0171 static int readInt(const QDomElement &element, const QString& name, int* err) 0172 { //out of class, cannot be static 0173 *err = 1; 0174 QString Str = element.attribute(name); 0175 if(Str.isNull()) 0176 return 0; 0177 bool noFailure = true; 0178 int num = Str.toInt(&noFailure, 0); 0179 if(!noFailure) 0180 return 0; 0181 *err = 0; 0182 return num; 0183 } 0184 0185 SKGraph* Serializer::deserializeGraph(const QDomElement &element) { 0186 bool noFailure = true; 0187 0188 QString orderStr = element.attribute(QStringLiteral("order")); 0189 if(orderStr.isNull()) 0190 return nullptr; 0191 // Allow symbolic values for Mathdoku, set from user-config dialog. 0192 int order = (orderStr == QStringLiteral("Mathdoku")) ? 0193 Settings::mathdokuSize() : 0194 orderStr.toInt(&noFailure, 0); 0195 if(!noFailure) 0196 return nullptr; 0197 0198 QString type = element.attribute(QStringLiteral("type")); 0199 if(type.isNull()) 0200 return nullptr; 0201 0202 if(type == QLatin1String("sudoku")) { 0203 auto *graph = new SKGraph(order, TypeSudoku); 0204 graph->initSudoku(); 0205 return graph; 0206 } else if(type == QLatin1String("roxdoku")) { 0207 auto *graph = new SKGraph(order, TypeRoxdoku); 0208 graph->initRoxdoku(); 0209 return graph; 0210 } else if(type == QLatin1String("custom")) { 0211 int err=0; 0212 int sizeX; 0213 int sizeY; 0214 int sizeZ; 0215 if (orderStr != QStringLiteral("Mathdoku")) { 0216 sizeX = readInt(element,QStringLiteral("sizeX"),&err); 0217 sizeY = readInt(element,QStringLiteral("sizeY"),&err); 0218 } 0219 else { 0220 // In Mathdoku, there are row and column groups only. 0221 sizeX = order; 0222 sizeY = order; 0223 } 0224 sizeZ = readInt(element,QStringLiteral("sizeZ"),&err); 0225 0226 QString name = element.attribute(QStringLiteral("name")); 0227 QString typeName = element.attribute(QStringLiteral("specific-type")); 0228 SudokuType puzzleType = Plain; // Default puzzle-type. 0229 for (int n = 0; n < EndSudokuTypes; n++) { 0230 QString lookup = QLatin1String(typeNames [n]); 0231 if (QString::compare (typeName, lookup, Qt::CaseInsensitive) 0232 == 0) { 0233 puzzleType = types [n]; 0234 break; 0235 } 0236 } 0237 0238 if(err==1) return nullptr; 0239 if(sizeX<1 || sizeY<1 || sizeZ<1) return nullptr; 0240 0241 auto* graph = new SKGraph(order, TypeCustom); 0242 graph->initCustom(name, puzzleType, order, 0243 sizeX, sizeY, sizeZ); 0244 0245 QDomNode child = element.firstChild(); 0246 while (!child.isNull()) { 0247 if(child.isElement()) { 0248 QDomElement e = child.toElement(); 0249 QString tag = e.tagName(); 0250 if (tag == QLatin1String("clique")) { 0251 QString sz = e.attribute(QStringLiteral("size")); 0252 if(! deserializeClique(graph, sz, e.text())) { 0253 delete graph; // Error return. 0254 return nullptr; 0255 } 0256 } 0257 else if (tag == QLatin1String("sudokugroups")) { 0258 graph->initSudokuGroups( 0259 e.attribute(QStringLiteral("at"), QStringLiteral("0")).toInt(), 0260 (e.attribute(QStringLiteral("withblocks"),QStringLiteral("1")) == QLatin1String("1"))); 0261 } 0262 else if (tag == QLatin1String("roxdokugroups")) { 0263 graph->initRoxdokuGroups( 0264 e.attribute(QStringLiteral("at"), QStringLiteral("0")).toInt()); 0265 } 0266 else if (tag == QLatin1String("cage")) { 0267 if(! deserializeCage(graph, e)) { 0268 delete graph; // Error return. 0269 return nullptr; 0270 } 0271 } 0272 } 0273 child = child.nextSibling(); 0274 } 0275 graph->endCustom(); // Finalise the structure of the graph. 0276 return graph; 0277 } 0278 return nullptr; 0279 } 0280 0281 bool Serializer::deserializeClique(SKGraph * graph, const QString & size, 0282 const QString & text) { 0283 // A group (or clique) should have a size followed by that number of 0284 // indices of cells that are members of the Group. Normally size is 0285 // equal to m_order (e.g. 4, 9, 16, 25). 0286 0287 int cellCount = 0; 0288 if(! size.isNull()) { 0289 cellCount = size.toInt(); 0290 } 0291 if (cellCount <= 0) { 0292 return false; 0293 } 0294 0295 const QStringList splitData = text.split(QStringLiteral(" "), Qt::SkipEmptyParts); 0296 QList<int> data; 0297 data.clear(); 0298 for (const QString &s : splitData) { 0299 --cellCount; 0300 data << s.toInt(); 0301 if(cellCount <= 0) { 0302 break; 0303 } 0304 } 0305 graph->addCliqueStructure(data); 0306 return true; 0307 } 0308 0309 bool Serializer::deserializeCage(SKGraph * graph, const QDomElement & e) { 0310 QString sizeStr = e.attribute(QStringLiteral("size")); 0311 QString text = e.text(); 0312 CageOperator op = (CageOperator) (e.attribute(QStringLiteral("operator")).toInt()); 0313 int target = e.attribute(QStringLiteral("value")).toInt(); 0314 int size = 0; 0315 QList<int> cage; 0316 if(! sizeStr.isNull()) { 0317 size = sizeStr.toInt(); 0318 } 0319 if (size <= 0) { 0320 return false; 0321 } 0322 0323 const QStringList cells = text.split(QStringLiteral(" "), Qt::SkipEmptyParts); 0324 cage.clear(); 0325 for (const QString& s : cells) { 0326 cage << s.toInt(); 0327 size--; 0328 if (size <= 0) { 0329 break; 0330 } 0331 } 0332 0333 graph->addCage(cage, op, target); 0334 return true; 0335 } 0336 0337 QList<HistoryEvent> Serializer::deserializeHistory(const QDomElement &element) { 0338 QList<HistoryEvent> history; 0339 0340 QDomNode child = element.firstChild(); 0341 while (!child.isNull()) { 0342 if(child.isElement()) { 0343 if(child.nodeName() == QLatin1String("simple-event")) { 0344 history.append(deserializeSimpleHistoryEvent(child.toElement())); 0345 } else if(child.nodeName() == QLatin1String("complex-event")) { 0346 history.append(deserializeComplexHistoryEvent(child.toElement())); 0347 } 0348 } 0349 child = child.nextSibling(); 0350 } 0351 return history; 0352 } 0353 0354 HistoryEvent Serializer::deserializeSimpleHistoryEvent(const QDomElement &element) { 0355 QString indexStr = element.attribute(QStringLiteral("index")); 0356 QString markerStr = element.attribute(QStringLiteral("markers")); 0357 QString valueStr = element.attribute(QStringLiteral("value")); 0358 bool given = element.attribute(QStringLiteral("given")) == QLatin1String("true"); 0359 bool noFailure = true; 0360 0361 int index = indexStr.toInt(&noFailure, 0); 0362 if(!noFailure) 0363 return HistoryEvent(); 0364 0365 0366 if(markerStr.isNull() == valueStr.isNull()) 0367 return HistoryEvent(); 0368 0369 0370 if(!markerStr.isNull()) { 0371 QBitArray markers(markerStr.length()); 0372 for(int i = 0; i < markerStr.length(); ++i) 0373 markers[i] = markerStr[i] != QLatin1Char('0'); 0374 0375 return HistoryEvent(index, CellInfo(markers)); 0376 } else { 0377 int value = valueStr.toInt(&noFailure, 0); 0378 if(!noFailure) 0379 return HistoryEvent(); 0380 0381 if(given) { 0382 return HistoryEvent(index, CellInfo(GivenValue, value)); 0383 } else { 0384 return HistoryEvent(index, CellInfo(CorrectValue, value)); 0385 } 0386 } 0387 0388 return HistoryEvent(); 0389 } 0390 0391 HistoryEvent Serializer::deserializeComplexHistoryEvent(const QDomElement /*element*/&) { 0392 // TODO implement this 0393 return HistoryEvent(); 0394 } 0395 0396 SKGraph *Serializer::loadCustomShape(const QUrl& url, QWidget* window, QString& errorMsg) { 0397 Q_UNUSED(window); 0398 if ( url.isEmpty() ) { 0399 errorMsg = i18n("Unable to download file: URL is empty."); 0400 return nullptr; 0401 } 0402 QDomDocument doc; 0403 QFile file(url.toLocalFile()); 0404 0405 if ( !file.open(QIODevice::ReadOnly) ) { 0406 errorMsg = i18n("Unable to open file."); 0407 return nullptr; 0408 } 0409 0410 const QDomDocument::ParseResult parseResult = doc.setContent(&file); 0411 if (!parseResult) { 0412 qDebug() << "Error " << parseResult.errorMessage << " from line " << parseResult.errorLine << ":" << parseResult.errorColumn << " from file " << url.toString(); 0413 errorMsg = i18n("Cannot read XML file on line %1", parseResult.errorLine); 0414 0415 return nullptr; 0416 } 0417 0418 QDomNode child = doc.documentElement().firstChild(); 0419 while (!child.isNull()) { 0420 if(child.isElement()) { 0421 if(child.nodeName() == QLatin1String("graph")) { 0422 return deserializeGraph(child.toElement()); 0423 } 0424 } 0425 child = child.nextSibling(); 0426 } 0427 0428 return nullptr; 0429 } 0430 0431 Game Serializer::load(const QUrl& url, QWidget* window, QString& errorMsg) { 0432 if ( url.isEmpty() ) return Game(); 0433 QDomDocument doc; 0434 0435 KIO::StoredTransferJob *downloadJob = KIO::storedGet(url); 0436 KJobWidgets::setWindow(downloadJob, window); 0437 downloadJob->exec(); 0438 0439 if( downloadJob->error() ) { 0440 errorMsg = i18n("Unable to download file."); 0441 return Game(); 0442 } 0443 0444 const QDomDocument::ParseResult parseResult = doc.setContent(downloadJob->data()); 0445 if (!parseResult) { 0446 errorMsg = i18n("Cannot read XML file on line %1", parseResult.errorLine); 0447 return Game(); 0448 } 0449 0450 // used to ensure, that there is only one game 0451 bool hasGame = false; 0452 Game game; 0453 0454 QDomNode child = doc.documentElement().firstChild(); 0455 while (!child.isNull()) { 0456 if(child.isElement()) { 0457 if(child.nodeName() == QLatin1String("game")) { 0458 if(hasGame) 0459 return Game(); 0460 0461 game = deserializeGame(child.toElement()); 0462 hasGame = true; 0463 } 0464 } 0465 child = child.nextSibling(); 0466 } 0467 0468 return game; 0469 } 0470 0471 bool Serializer::serializeGame(QDomElement& parent, const Game& game) { 0472 QDomElement element = parent.ownerDocument().createElement(QStringLiteral("game")); 0473 element.setAttribute(QStringLiteral("had-help"), game.userHadHelp()); 0474 element.setAttribute(QStringLiteral("msecs-elapsed"), game.msecsElapsed()); 0475 serializePuzzle(element, game.puzzle()); 0476 serializeHistory(element, game); 0477 parent.appendChild(element); 0478 return true; 0479 } 0480 0481 bool Serializer::serializePuzzle(QDomElement& parent, const Puzzle* puzzle) { 0482 QString contentStr; 0483 0484 QDomDocument doc = parent.ownerDocument(); 0485 QDomElement element = doc.createElement(QStringLiteral("puzzle")); 0486 serializeGraph(element, puzzle->graph()); 0487 0488 for(int i = 0; i < puzzle->size(); ++i) { 0489 contentStr += Symbols::ioValue2Symbol(puzzle->value(i)); 0490 } 0491 0492 QDomElement content = doc.createElement(QStringLiteral("values")); 0493 content.appendChild(doc.createTextNode(contentStr)); 0494 element.appendChild(content); 0495 0496 if(puzzle->hasSolution()) { 0497 contentStr = QString(); 0498 for(int i = 0; i < puzzle->size(); ++i) { 0499 contentStr += Symbols::ioValue2Symbol(puzzle->solution(i)); 0500 } 0501 content = doc.createElement(QStringLiteral("solution")); 0502 content.appendChild(doc.createTextNode(contentStr)); 0503 element.appendChild(content); 0504 } 0505 0506 parent.appendChild(element); 0507 return true; 0508 } 0509 0510 bool Serializer::serializeGraph(QDomElement &parent, const SKGraph *graph) 0511 { 0512 QDomElement element = parent.ownerDocument().createElement(QStringLiteral("graph")); 0513 element.setAttribute(QStringLiteral("order"), graph->order()); 0514 0515 GameType type = graph->type(); 0516 element.setAttribute(QStringLiteral("type") , (type == TypeSudoku) ? QStringLiteral("sudoku") : 0517 (type == TypeRoxdoku) ? QStringLiteral("roxdoku") : QStringLiteral("custom")); 0518 0519 int n = -1; 0520 SudokuType puzzleType = graph->specificType(); 0521 for (n = 0; n < EndSudokuTypes; n++) { 0522 if (puzzleType == types [n]) { 0523 break; 0524 } 0525 } 0526 element.setAttribute(QStringLiteral("specific-type"), (n < 0) ? QStringLiteral("Plain") : QLatin1String(typeNames[n])); 0527 0528 if(type == TypeCustom) { 0529 element.setAttribute(QStringLiteral("name"), graph->name()); 0530 element.setAttribute(QStringLiteral("sizeX"), graph->sizeX()); 0531 element.setAttribute(QStringLiteral("sizeY"), graph->sizeY()); 0532 element.setAttribute(QStringLiteral("sizeZ"), graph->sizeZ()); 0533 0534 for (int n = 0; n < graph->structureCount(); n++) { 0535 QDomElement e; 0536 SKGraph::StructureType sType = graph->structureType(n); 0537 switch (sType) { 0538 case SKGraph::SudokuGroups: 0539 e = parent.ownerDocument().createElement(QStringLiteral("sudokugroups")); 0540 e.setAttribute(QStringLiteral("at"), graph->structurePosition(n)); 0541 e.setAttribute(QStringLiteral("withblocks"), 0542 graph->structureHasBlocks(n) ? QStringLiteral("1") : QStringLiteral("0")); 0543 break; 0544 case SKGraph::RoxdokuGroups: 0545 e = parent.ownerDocument().createElement(QStringLiteral("roxdokugroups")); 0546 e.setAttribute(QStringLiteral("at"), graph->structurePosition(n)); 0547 break; 0548 case SKGraph::Clique: 0549 e = parent.ownerDocument().createElement(QStringLiteral("clique")); 0550 int cNum = graph->structurePosition(n); 0551 int cSize = graph->clique(cNum).size(); 0552 e.setAttribute(QStringLiteral("size"), cSize); 0553 0554 // Serialize the cell-numbers in the clique (or group). 0555 QString contentStr = QLatin1String(""); 0556 for(int j=0; j < cSize; j++) { 0557 contentStr += QString::number 0558 (graph->clique(cNum).at(j)) + QLatin1Char(' '); 0559 } 0560 e.appendChild(parent.ownerDocument(). 0561 createTextNode(contentStr)); 0562 break; 0563 } 0564 element.appendChild(e); 0565 } 0566 0567 // Add cages if this is a Mathdoku or Killer Sudoku puzzle. 0568 for (int n = 0; n < graph->cageCount(); n++) { 0569 QDomElement e = parent.ownerDocument().createElement(QStringLiteral("cage")); 0570 const QList<int> cage = graph->cage(n); 0571 e.setAttribute(QStringLiteral("operator"), graph->cageOperator(n)); 0572 e.setAttribute(QStringLiteral("value"), graph->cageValue(n)); 0573 e.setAttribute(QStringLiteral("size"), cage.size()); 0574 0575 // Serialize the cell-numbers in the cage. 0576 QString contentStr = QStringLiteral(" "); 0577 for (const int cell : cage) { 0578 contentStr += QString::number(cell) + QLatin1Char(' '); 0579 } 0580 e.appendChild(parent.ownerDocument(). 0581 createTextNode(contentStr)); 0582 element.appendChild(e); 0583 } 0584 } 0585 0586 parent.appendChild(element); 0587 return true; 0588 } 0589 0590 bool Serializer::serializeHistory(QDomElement& parent, const Game& game) { 0591 QDomElement element = parent.ownerDocument().createElement(QStringLiteral("history")); 0592 0593 for(int i = 0; i < game.historyLength(); ++i) { 0594 if(!serializeHistoryEvent(element, game.historyEvent(i))) 0595 return false; 0596 } 0597 0598 parent.appendChild(element); 0599 return true; 0600 } 0601 0602 bool Serializer::serializeHistoryEvent(QDomElement& parent, const HistoryEvent& event) { 0603 QDomElement element; 0604 0605 const QList<int>& indices = event.cellIndices(); 0606 const QList<CellInfo>& changes = event.cellChanges(); 0607 0608 if(indices.count() == 0) { 0609 return true; 0610 } else if(indices.count() == 1) { 0611 element = parent.ownerDocument().createElement(QStringLiteral("simple-event")); 0612 0613 element.setAttribute(QStringLiteral("index"), indices[0]); 0614 switch(changes[0].state()) { 0615 case GivenValue: 0616 element.setAttribute(QStringLiteral("given"), QStringLiteral("true")); 0617 element.setAttribute(QStringLiteral("value"), changes[0].value()); 0618 break; 0619 case ObviouslyWrong: 0620 case WrongValue: 0621 case CorrectValue: 0622 element.setAttribute(QStringLiteral("value"), changes[0].value()); 0623 break; 0624 case Marker: { 0625 QString str; 0626 0627 QBitArray markers = changes[0].markers(); 0628 for(int j = 0; j < markers.size(); ++j) { 0629 str += markers[j] ? QLatin1Char('1') : QLatin1Char('0'); 0630 } 0631 0632 element.setAttribute(QStringLiteral("markers"), str); 0633 } break; 0634 } 0635 } else { 0636 element = parent.ownerDocument().createElement(QStringLiteral("complex-event")); 0637 for(int i = 0; i < indices.count(); ++i) { 0638 QDomElement subElement = parent.ownerDocument().createElement(QStringLiteral("simple-event")); 0639 0640 subElement.setAttribute(QStringLiteral("index"), indices[i]); 0641 switch(changes[i].state()) { 0642 case GivenValue: 0643 subElement.setAttribute(QStringLiteral("given"), QStringLiteral("true")); 0644 subElement.setAttribute(QStringLiteral("value"), changes[i].value()); 0645 break; 0646 case ObviouslyWrong: 0647 case WrongValue: 0648 case CorrectValue: 0649 subElement.setAttribute(QStringLiteral("value"), changes[i].value()); 0650 break; 0651 case Marker: { 0652 QString str; 0653 0654 QBitArray markers = changes[i].markers(); 0655 for(int j = 0; j < markers.size(); ++j) { 0656 str += markers[i] ? QLatin1Char('1') : QLatin1Char('0'); 0657 } 0658 0659 subElement.setAttribute(QStringLiteral("markers"), str); 0660 } break; 0661 } 0662 0663 element.appendChild(subElement); 0664 } 0665 } 0666 0667 parent.appendChild(element); 0668 return true; 0669 } 0670 0671 bool Serializer::store(const Game& game, const QUrl& url, QWidget* window, QString& errorMsg) { 0672 QDomDocument doc( QStringLiteral("ksudoku") ); 0673 QDomElement root = doc.createElement( QStringLiteral("ksudoku") ); 0674 doc.appendChild( root ); 0675 0676 serializeGame(root, game); 0677 0678 QTemporaryFile file; 0679 if ( !file.open() ) { 0680 errorMsg = i18n("Unable to create temporary file."); 0681 return false; 0682 } 0683 0684 QTextStream stream(&file); 0685 stream << doc.toString(); 0686 stream.flush(); 0687 0688 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(file.fileName()), url, -1, KIO::Overwrite); 0689 KJobWidgets::setWindow(copyJob , window); 0690 copyJob->exec(); 0691 if(copyJob->error()) 0692 { 0693 errorMsg = i18n("Unable to upload file."); 0694 return false; 0695 } 0696 return true; 0697 } 0698 0699 }