When settings change, all tabs are notified. Fixes ticket #32.
[tagua/yd.git] / src / game.cpp
blob32afe36c5691b5b711d80b9fa5d50228d9fc635d
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 */
11 #include <iostream>
12 #include <map>
13 #ifdef Q_CC_MSVC
14 #pragma warning( push )
15 #pragma warning( disable : 4100 )
16 #include <boost/variant.hpp>
17 #pragma warning( pop )
18 #else
19 #include <boost/variant.hpp>
20 #endif
21 #ifndef NO_PGN
22 #include "variants.h"
23 #include "pgnparser.h"
24 #include "tagua.h"
25 #endif //NO_PGN
26 #include "game.h"
27 #include "game_p.h"
30 using namespace GamePrivate;
33 Game::Game()
34 : current(-1)
35 , undo_pos(0) {
38 Game::~Game() {
41 void Game::onAdded(const Index&) {
44 void Game::onRemoved(const Index&) {
47 void Game::onEntryChanged(const Index&, int) {
50 void Game::onPromoteVariation(const Index&, int) {
53 void Game::onSetComment(const Index&, const QString&) {
56 void Game::onSetVComment(const Index&, int, const QString&) {
59 void Game::onCurrentIndexChanged(const Index&) {
62 void Game::onAvailableUndo(bool) {
65 void Game::onAvailableRedo(bool) {
68 Entry* Game::fetch(const Index& ix) {
69 int at;
70 History *vec = fetchRef(ix, &at);
71 return vec ? &(*vec)[at] : NULL;
74 const Entry* Game::fetch(const Index& ix) const {
75 int at;
76 const History *vec = fetchRef(ix, &at);
77 return vec ? &(*vec)[at] : NULL;
80 History* Game::fetchRef(const Index& ix, int* idx) {
81 if(ix.num_moves >= (int)history.size() || ix.num_moves < 0 )
82 return NULL;
84 History* aretv = &history;
85 Entry* retv = &history[ix.num_moves];
86 if(idx) *idx = ix.num_moves;
88 for(int i=0; i<(int)ix.nested.size();i++) {
89 Variations::iterator it = retv->variations.find(ix.nested[i].variation);
90 if(it == retv->variations.end() || ix.nested[i].num_moves >= (int)it->second.size()
91 || ix.nested[i].num_moves < 0 )
92 return NULL;
94 aretv = &it->second;
95 retv = &it->second[ix.nested[i].num_moves];
96 if(idx) *idx = ix.nested[i].num_moves;
98 return aretv;
101 const History* Game::fetchRef(const Index& ix, int* idx) const {
102 return const_cast<const History*>(const_cast<Game*>(this)->fetchRef(ix, idx));
105 void Game::testMove(const Index& ix) {
106 if (ix != Index(0)) {
107 Entry *e1 = fetch(ix.prev());
108 Entry *e2 = fetch(ix);
109 if(!e1 || !e2 || !e1->position || !e2->move)
110 return;
112 if (!e1->position->testMove(e2->move))
113 ERROR("invalid move added to game history!");
117 void Game::testMove() {
118 testMove(current);
121 void Game::saveUndo(const UndoOp& op) {
122 bool redo = undo_pos < (int)undo_history.size();
124 while(undo_pos < (int)undo_history.size())
125 undo_history.pop_back();
126 undo_history.push_back(op);
127 undo_pos++;
129 if(undo_pos == 1)
130 onAvailableUndo(true);
131 if(redo)
132 onAvailableRedo(false);
136 Index Game::index() const {
137 return current;
140 Index Game::lastMainlineIndex() const {
141 return Index(history.size()-1);
144 bool Game::containsIndex(const Index& index) const {
145 return !!fetch(index);
148 MovePtr Game::move() const {
149 return move(current);
152 MovePtr Game::move(const Index& index) const {
153 Entry *e = (Entry*)fetch(index);
154 if(!e) {
155 ERROR("Index out of range!");
156 return MovePtr();
158 return e->move;
161 PositionPtr Game::position() const {
162 return position(current);
165 PositionPtr Game::position(const Index& index) const {
166 Entry *e = (Entry*)fetch(index);
167 if(!e) {
168 ERROR("Index " << index << " out of range!");
169 return PositionPtr();
171 return e->position;
174 QString Game::comment() const {
175 return comment(current);
178 QString Game::comment(const Index& index) const {
179 const Entry *e = fetch(index);
180 if(!e) {
181 ERROR("Index out of range!");
182 return QString();
184 return e->comment;
187 void Game::reset(PositionPtr pos) {
188 Q_ASSERT(pos);
190 undo_pos = 0;
191 undo_history.clear();
192 history.clear();
193 history.push_back( Entry(MovePtr(), pos) );
194 current = Index(0);
195 onCurrentIndexChanged();
198 void Game::undo() {
199 if(undo_pos <= 0) {
200 ERROR("Cannot undo at the beginning of the undo history!");
201 return;
204 bool last_undo = undo_pos == 1;
205 bool now_redo = undo_pos == (int)undo_history.size();
207 undo_pos--;
208 UndoOp* op = &(undo_history[undo_pos]);
210 if(boost::get<UndoAdd>(op)) {
211 UndoAdd *a = boost::get<UndoAdd>(op);
213 if(a->index.atVariationStart() ) {
214 Entry* e = fetch(a->index.prev());
215 Q_ASSERT(e);
217 int v = a->index.nested[a->index.nested.size()-1].variation;
218 Q_ASSERT(e->variations.count(v) == 1);
219 Q_ASSERT(e->variations[v].size() == 1);
221 e->variations.erase(v);
223 else {
224 int at;
225 std::vector<Entry>* vec = fetchRef(a->index, &at);
226 Q_ASSERT(vec);
227 Q_ASSERT((int)vec->size() == at+1);
229 vec->pop_back();
232 if(current == a->index) {
233 current = current.prev();
234 onCurrentIndexChanged();
237 onRemoved(a->index);
239 else if(boost::get<UndoPromote>(op)) {
240 UndoPromote *p = boost::get<UndoPromote>(op);
242 int at;
243 std::vector<Entry>* vec = fetchRef(p->index, &at);
244 Q_ASSERT(vec);
245 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
247 History vold = (*vec)[at].variations[p->variation];
248 History vnew;
249 for(int i=at+1; i<(int)vec->size(); i++)
250 vnew.push_back((*vec)[i]);
251 while((int)vec->size()>at+1)
252 vec->pop_back();
253 for(int i=0; i<(int)vold.size(); i++)
254 vec->push_back(vold[i]);
255 (*vec)[at].variations[p->variation] = vnew;
257 current = current.flipVariation(p->index, p->variation);
258 onPromoteVariation(p->index, p->variation);
259 //onCurrentIndexChanged();
261 else if(boost::get<UndoTruncate>(op)) {
262 UndoTruncate *t = boost::get<UndoTruncate>(op);
264 int at;
265 std::vector<Entry>* vec = fetchRef(t->index, &at);
266 Q_ASSERT(vec);
267 Q_ASSERT((int)vec->size() == at+1);
268 Q_ASSERT((*vec)[at].variations.empty());
270 for(int i=0;i<(int)t->history.size();i++)
271 vec->push_back(t->history[i]);
272 (*vec)[at].variations = t->variations;
273 (*vec)[at].vcomments = t->vcomments;
275 if(t->history.size())
276 onAdded(t->index.next());
277 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
278 onAdded(t->index.next(it->first));
279 for(VComments::iterator it = t->vcomments.begin(); it != t->vcomments.end(); ++it)
280 onSetVComment(t->index, it->first, it->second);
282 else if(boost::get<UndoRemove>(op)) {
283 UndoRemove *r = boost::get<UndoRemove>(op);
285 Entry *e = fetch(r->index);
286 e->variations[r->variation] = r->history;
287 onAdded(r->index.next(r->variation));
288 if(!r->vcomment.isEmpty()) {
289 e->vcomments[r->variation] = r->vcomment;
290 onSetVComment(r->index, r->variation, r->vcomment);
293 else if(boost::get<UndoClear>(op)) {
294 UndoClear *c = boost::get<UndoClear>(op);
296 Entry *e = fetch(c->index);
297 e->variations = c->variations;
298 e->vcomments = c->vcomments;
299 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
300 onAdded(c->index.next(it->first));
301 for(VComments::iterator it = c->vcomments.begin(); it != c->vcomments.end(); ++it)
302 onSetVComment(c->index, it->first, it->second);
304 else if(boost::get<UndoSetComment>(op)) {
305 UndoSetComment *sc = boost::get<UndoSetComment>(op);
306 Entry *e = fetch(sc->index);
307 Q_ASSERT(e);
309 if(sc->variation == -1) {
310 e->comment = sc->old_comment;
311 onSetComment(sc->index, sc->old_comment);
313 else {
314 if(sc->old_comment.isEmpty())
315 e->vcomments.erase(sc->variation);
316 else
317 e->vcomments[sc->variation] = sc->old_comment;
318 onSetVComment(sc->index, sc->variation, sc->old_comment);
322 if(last_undo)
323 onAvailableUndo(false);
324 if(now_redo)
325 onAvailableRedo(true);
328 void Game::redo() {
329 if(undo_pos >= (int)undo_history.size()) {
330 ERROR("Cannot redo at the end of the undo history!");
331 return;
334 bool now_undo = undo_pos == 0;
335 bool last_redo = undo_pos == (int)undo_history.size()-1;
337 UndoOp* op = &(undo_history[undo_pos]);
338 undo_pos++;
340 if(boost::get<UndoAdd>(op)) {
341 UndoAdd *a = boost::get<UndoAdd>(op);
343 if(a->index.atVariationStart() ) {
344 Entry* e = fetch(a->index.prev());
345 Q_ASSERT(e);
347 int v = a->index.nested[a->index.nested.size()-1].variation;
348 Q_ASSERT(e->variations.count(v) == 0);
350 History h;
351 h.push_back(a->entry);
352 e->variations[v] = h;
354 else {
355 int at;
356 std::vector<Entry>* vec = fetchRef(a->index.prev(), &at);
357 Q_ASSERT(vec);
358 Q_ASSERT((int)vec->size() == at+1);
360 vec->push_back(a->entry);
363 onAdded(a->index);
365 else if(boost::get<UndoPromote>(op)) {
366 UndoPromote *p = boost::get<UndoPromote>(op);
368 int at;
369 std::vector<Entry>* vec = fetchRef(p->index, &at);
371 Q_ASSERT(vec);
372 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
373 History vold = (*vec)[at].variations[p->variation];
374 History vnew;
375 for(int i=at+1; i<(int)vec->size(); i++)
376 vnew.push_back((*vec)[i]);
377 while((int)vec->size()>at+1)
378 vec->pop_back();
379 for(int i=0; i<(int)vold.size(); i++)
380 vec->push_back(vold[i]);
381 (*vec)[at].variations[p->variation] = vnew;
383 current = current.flipVariation(p->index, p->variation);
384 onPromoteVariation(p->index, p->variation);
385 //onCurrentIndexChanged();
387 else if(boost::get<UndoTruncate>(op)) {
388 UndoTruncate *t = boost::get<UndoTruncate>(op);
390 int at;
391 std::vector<Entry>* vec = fetchRef(t->index, &at);
392 Q_ASSERT(vec);
393 Q_ASSERT((int)vec->size() == at+1+(int)t->history.size());
395 while((int)vec->size() > at+1)
396 vec->pop_back();
397 (*vec)[at].variations.clear();
398 (*vec)[at].vcomments.clear();
400 if(current > t->index) {
401 current = t->index;
402 onCurrentIndexChanged();
405 if(t->history.size())
406 onRemoved(t->index.next());
407 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
408 onRemoved(t->index.next(it->first));
410 else if(boost::get<UndoRemove>(op)) {
411 UndoRemove *r = boost::get<UndoRemove>(op);
413 Entry *e = fetch(r->index);
414 e->variations.erase(r->variation);
415 e->vcomments.erase(r->variation);
416 onRemoved(r->index.next(r->variation));
418 else if(boost::get<UndoClear>(op)) {
419 UndoClear *c = boost::get<UndoClear>(op);
421 Entry *e = fetch(c->index);
422 e->variations.clear();
423 e->vcomments.clear();
424 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
425 onRemoved(c->index.next(it->first));
427 else if(boost::get<UndoSetComment>(op)) {
428 UndoSetComment *sc = boost::get<UndoSetComment>(op);
429 Entry *e = fetch(sc->index);
430 Q_ASSERT(e);
432 if(sc->variation == -1) {
433 e->comment = sc->new_comment;
434 onSetComment(sc->index, sc->new_comment);
436 else {
437 if(sc->new_comment.isEmpty())
438 e->vcomments.erase(sc->variation);
439 else
440 e->vcomments[sc->variation] = sc->new_comment;
441 onSetVComment(sc->index, sc->variation, sc->new_comment);
445 if(now_undo)
446 onAvailableUndo(true);
447 if(last_redo)
448 onAvailableRedo(false);
451 void Game::setComment(const QString& c) {
452 setComment(current, c);
455 void Game::setComment(const Index& ix, const QString& c) {
456 Entry* e = fetch(ix);
457 if(!e) {
458 ERROR("Invalid index!");
459 return;
461 if(e->comment == c)
462 return;
464 saveUndo(UndoSetComment(ix, -1, e->comment, c));
465 e->comment = c;
466 onSetComment(ix, c);
469 void Game::setVComment(const Index& ix, int v, const QString& c) {
470 Entry* e = fetch(ix);
471 if(!e) {
472 ERROR("Invalid index!");
473 return;
475 QString oc = e->vcomments.count(v) ? e->vcomments[v] : QString();
476 if(oc == c)
477 return;
479 saveUndo(UndoSetComment(ix, v, oc, c));
480 if(c.isEmpty())
481 e->vcomments.erase(v);
482 else
483 e->vcomments[v] = c;
484 onSetVComment(ix, v, c);
487 void Game::promoteVariation() {
488 promoteVariation(current);
491 void Game::promoteVariation(const Index& _ix) {
492 if(_ix.nested.size()==0) {
493 ERROR("Cannot promote main line!");
494 return;
496 Index ix = _ix;
497 int v = ix.nested[ix.nested.size()-1].variation;
498 ix.nested.pop_back();
500 promoteVariation(ix, v);
503 void Game::promoteVariation(const Index& ix, int v) {
504 int at;
505 std::vector<Entry>* vec = fetchRef(ix, &at);
506 Q_ASSERT(vec);
507 Q_ASSERT((*vec)[at].variations.count(v)==1);
509 History vold = (*vec)[at].variations[v];
510 History vnew;
511 for(int i=at+1; i<(int)vec->size(); i++)
512 vnew.push_back((*vec)[i]);
513 while((int)vec->size()>at+1)
514 vec->pop_back();
515 for(int i=0; i<(int)vold.size(); i++)
516 vec->push_back(vold[i]);
517 (*vec)[at].variations[v] = vnew;
519 saveUndo(UndoPromote(ix, v));
520 current = current.flipVariation(ix, v);
521 onPromoteVariation(ix, v);
522 //don't call onCurrentIndexChanged(), as the position did not change actually
525 void Game::removeVariation(int v) {
526 removeVariation(current, v);
529 void Game::removeVariation(const Index& _ix) {
530 if(_ix.nested.size()==0) {
531 ERROR("Cannot remove main line!");
532 return;
534 Index ix = _ix;
535 int v = ix.nested[ix.nested.size()-1].variation;
536 ix.nested.pop_back();
538 removeVariation(ix, v);
541 void Game::removeVariation(const Index& ix, int v) {
542 Entry* e = fetch(ix);
544 saveUndo(UndoRemove(ix, v, e->variations[v],
545 e->vcomments.count(v) ? e->vcomments[v] : QString() ));
546 e->variations.erase(v);
547 e->vcomments.erase(v);
549 onRemoved(ix.next(v));
550 if(current >= ix.next(v)) {
551 current = ix;
552 onCurrentIndexChanged();
556 void Game::clearVariations() {
557 clearVariations(current);
560 void Game::clearVariations(const Index& ix) {
561 Entry* e = fetch(ix);
563 UndoClear uc(ix, e->variations, e->vcomments);
564 saveUndo(uc);
565 e->variations.clear();
566 e->vcomments.clear();
568 for(Variations::iterator it = uc.variations.begin(); it != uc.variations.end(); ++it)
569 onRemoved(ix.next(it->first));
570 if(current > ix && !(current >= ix.next())) {
571 current = ix;
572 onCurrentIndexChanged();
576 void Game::truncate() {
577 truncate(current);
580 void Game::truncate(const Index& ix) {
581 int at;
582 History* vec = fetchRef(ix, &at);
583 if(!vec) {
584 ERROR("Truncating at an unexisting index!");
585 return;
588 Entry *e = &(*vec)[at];
589 UndoTruncate undo(ix);
590 for(int i=at+1; i<(int)vec->size();i++)
591 undo.history.push_back((*vec)[i]);
592 while((int)vec->size()>at+1)
593 vec->pop_back();
595 undo.variations = e->variations;
596 undo.vcomments = e->vcomments;
597 saveUndo(undo);
598 e->variations.clear();
599 e->vcomments.clear();
601 if(undo.history.size())
602 onRemoved(undo.index.next());
603 for(Variations::iterator it = undo.variations.begin(); it != undo.variations.end(); ++it)
604 onRemoved(undo.index.next(it->first));
606 if(current > ix) {
607 current = ix;
608 onCurrentIndexChanged();
612 void Game::add(MovePtr m, PositionPtr pos) {
613 Q_ASSERT(pos);
615 Index old_c = current;
616 int at;
617 std::vector<Entry>* vec = fetchRef(current, &at);
618 Q_ASSERT(vec);
620 /* add the move on the mainline */
621 if((int)vec->size() <= at+1 ) {
622 Q_ASSERT((int)vec->size() == at+1);
623 vec->push_back(Entry(m, pos));
624 current = current.next();
625 testMove();
626 saveUndo(UndoAdd(current, Entry(m, pos)));
627 onAdded(current);
628 onCurrentIndexChanged(old_c);
630 /* we are playing the move that is already next in the mainline */
631 else if( (*vec)[at+1].position && (*vec)[at+1].position->equals(pos)) {
632 current = current.next();
633 onCurrentIndexChanged(old_c);
634 /* no need to test the move */
636 else {
637 Entry *e = fetch(current);
638 Q_ASSERT(e);
640 /* check if a variations with this move already exists. */
641 for(Variations::iterator it = e->variations.begin(); it != e->variations.end(); ++it)
642 if(it->second.size() > 0 && it->second[0].position
643 && it->second[0].position->equals(pos) ) {
644 current = current.next(it->first);
645 onCurrentIndexChanged(old_c);
647 return;
650 int var_id = e->last_var_id++;
651 e->variations[var_id].push_back(Entry(m, pos));
652 current = current.next(var_id);
653 testMove();
654 saveUndo(UndoAdd(current, Entry(m, pos)));
655 onAdded(current);
656 onCurrentIndexChanged(old_c);
660 bool Game::insert(MovePtr m, PositionPtr pos, const Index& at) {
661 Entry *e = fetch(at);
663 if(!e) {
664 if(at.nested.size() == 0) {
665 if(undo_history.size()) {
666 undo_pos = 0;
667 undo_history.clear();
669 int hs = history.size();
670 history.resize(at.num_moves + 1);
671 history[at.num_moves] = Entry(m, pos);
672 testMove(at);
673 onAdded(Index(hs));
674 return true;
676 else {
677 ERROR("Index out if range!");
678 return false;
682 if(undo_history.size()) {
683 undo_pos = 0;
684 undo_history.clear();
686 bool res = e->position && e->position->equals(pos);
687 e->move = m;
688 e->position = pos;
689 testMove(at);
690 testMove(at.next());
691 for (Variations::const_iterator it = e->variations.begin();
692 it != e->variations.end(); ++it)
693 testMove(at.next(it->first));
694 onEntryChanged(at);
695 return res;
698 bool Game::lastPosition() const {
699 return !fetch(current.next());
702 bool Game::back() {
703 if (current <= 0) return false; // first entry or uninitialized
704 Index old_c = current;
705 Index new_c = current.prev();
707 Entry *e = fetch(new_c);
708 if(!e || e->position == 0) return false; // gap immediately before current
709 current = new_c;
710 onCurrentIndexChanged(old_c);
712 return true;
715 bool Game::forward() {
716 Index old_c = current;
717 Index new_c = current.next();
719 Entry *e = fetch(new_c);
720 if(!e || e->position == 0) {
721 return false; // gap immediately before current
723 current = new_c;
724 onCurrentIndexChanged(old_c);
726 return true;
729 void Game::gotoFirst() {
730 Index old_c = current;
731 current = Index(0);
732 onCurrentIndexChanged(old_c);
735 void Game::gotoLast() {
736 int at;
737 std::vector<Entry>* vec = fetchRef(current, &at);
738 Q_ASSERT(vec);
739 Q_ASSERT((int)vec->size() > at);
741 if((int)vec->size() > at+1) {
742 Index old_c = current;
743 current = current.next(-1, vec->size()-1-at);
744 onCurrentIndexChanged(old_c);
748 bool Game::goTo(const Index& index) {
749 if (fetch(index)) {
750 Index old_c = current;
751 current = index;
752 onCurrentIndexChanged(old_c);
753 return true;
755 return false;
758 QString Game::variationPgn(const History& vec, const Entry& e,
759 int start, const Index& _ix) const {
760 Index ix = _ix;
761 QString res;
763 for (int i = start; i < static_cast<int>(vec.size()); i++) {
764 const Entry& preve = (i > start) ? vec[i-1] : e;
766 QString mv = (vec[i].move && preve.position) ?
767 vec[i].move->toString("compact", preve.position ) : "???";
768 #if 0
769 if (ix == current)
770 mv = "[[" + mv + "]]";
771 #endif
773 int n = ix.totalNumMoves()+1;
774 if(i==start || n%2==0)
775 mv = QString::number(n/2)+(n%2==1 ? ". ... " : ". ") + mv;
776 if (i > start)
777 mv = " " + mv;
779 res += mv;
781 if(!vec[i].comment.isEmpty())
782 res += " {" + vec[i].comment + "}";
784 if(i > 0) {
785 for(Variations::const_iterator it = vec[i-1].variations.begin();
786 it != vec[i-1].variations.end(); ++it) {
787 res += " (";
788 if(vec[i-1].vcomments.count(it->first))
789 res += "{" + vec[i-1].vcomments.find(it->first)->second + "} ";
790 res += variationPgn(it->second, vec[i - 1], 0,
791 ix.prev().next(it->first)) + ")";
795 ix = ix.next();
797 return res;
800 QString Game::pgn() const {
801 return variationPgn(history, history[0], 1, Index(1));
804 #ifndef NO_PGN
805 void Game::load(const PGN& pgn) {
806 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
807 VariantPtr vi;
809 if (var == pgn.m_tags.end()) {
810 vi = Variants::instance().get("chess");
812 else if (!(vi = Variants::instance().get(var->second))) {
813 ERROR("No such variant " << var->second);
814 return;
817 std::map<QString, QString>::const_iterator fen = pgn.m_tags.find("FEN");
818 PositionPtr pos;
820 if(var == pgn.m_tags.end()) {
821 pos = vi->createPosition();
822 pos->setup();
824 #if 0 // BROKEN
825 else if( !(pos = vi->createPositionFromFEN(fen->second))) {
826 ERROR("Wrong fen " << fen->second);
827 return;
829 #endif
831 //TODO: what about options? FEN rules?
833 load(pos, pgn);
836 void Game::load(PositionPtr pos, const PGN& pgn) {
837 current = Index(0);
838 undo_history.clear();
839 undo_pos = 0;
841 if(history.size()) {
842 Entry* fe = &history[0];
843 int old_history_size = history.size();
844 std::vector<int> v_ids;
846 while(history.size()>1)
847 history.pop_back();
848 for(Variations::const_iterator it = fe->variations.begin();
849 it != fe->variations.end(); ++it)
850 v_ids.push_back(it->first);
851 fe->variations.clear();
852 fe->vcomments.clear();
854 for(int i=0;i<(int)v_ids.size();i++)
855 onRemoved(Index(0).next(v_ids[i]));
856 if(old_history_size>1)
857 onRemoved(Index(1));
858 v_ids.clear();
859 history[0].position = pos;
861 else
862 history.push_back( Entry(MovePtr(), pos) );
864 QString vcomment;
865 std::vector<Index> var_stack;
866 bool var_start = false;
868 for (uint i = 0; i < pgn.m_entries.size(); i++) {
869 if(boost::get<QString>(pgn[i])) {
870 if(var_start)
871 vcomment += *boost::get<QString>(pgn[i]);
872 else {
873 Entry *e = fetch(current);
874 Q_ASSERT(e);
876 e->comment += *boost::get<QString>(pgn[i]);
879 else if(boost::get<PGN::BeginVariation>(pgn[i])) {
880 var_stack.push_back(current);
881 var_start = true;
883 else if(boost::get<PGN::EndVariation>(pgn[i])) {
884 if(var_stack.size() == 0) {
885 ERROR("Unexpected end variation!");
886 break;
888 current = var_stack[var_stack.size()-1];
889 var_stack.pop_back();
891 else if(boost::get<PGN::Move>(pgn[i])) {
892 const PGN::Move *pm = boost::get<PGN::Move>(pgn[i]);
894 int n = current.totalNumMoves()+1;
895 if(var_start) {
896 if(!pm->m_number)
897 current = current.prev();
898 else if(pm->m_number>n+1)
899 ERROR("Too far variation!");
900 else {
901 if(pm->m_number<n)
902 ERROR("Too near variation!");
903 current = current.prev(n + 1 - pm->m_number);
906 else if(pm->m_number && pm->m_number!=n+1)
907 ERROR("Move number mismatch!");
909 PositionPtr pos = position();
910 MovePtr m = pos->getMove(pm->m_move);
912 if(!m || !pos->testMove(m))
913 break;
915 PositionPtr newPos = pos->clone();
916 newPos->move(m);
918 int at;
919 History *vec = fetchRef(current, &at);
920 Q_ASSERT(vec);
922 if(var_start) {
923 Entry *e = &(*vec)[at];
924 int var_id = e->last_var_id++;
925 e->variations[var_id].push_back(Entry(m, newPos));
926 if(!vcomment.isEmpty()) {
927 e->vcomments[var_id] = vcomment;
928 vcomment = QString();
930 /* this is a hack, but the mainline should NEVER
931 be empty if there is a variation*/
932 if((int)vec->size() - 1 == at)
933 vec->push_back(Entry(m, newPos));
935 current = current.next(var_id);
937 else {
938 if((int)vec->size() - 1 == at)
939 vec->push_back(Entry(m, newPos));
940 else
941 (*vec)[at] = Entry(m, newPos);
943 current = current.next();
946 var_start = false;
950 if(history.size()>1)
951 onAdded(Index(1));
952 Entry* e = fetch(Index(0));
953 for(Variations::const_iterator it = e->variations.begin();
954 it != e->variations.end(); ++it)
955 onAdded(Index(0).next(it->first));
956 for(VComments::const_iterator it = e->vcomments.begin();
957 it != e->vcomments.end(); ++it)
958 onSetVComment(Index(0), it->first, it->second);
960 current = Index(0);
961 onCurrentIndexChanged();
963 #endif //NO_PGN