Removed unimplemented GUI actions.
[tagua/yd.git] / src / game.cpp
blob96cc2375523eb8454614c6d552574d254ed7b192
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 #include "variants.h"
22 #include "pgnparser.h"
23 #include "tagua.h"
24 #include "game.h"
25 #include "game_p.h"
28 using namespace GamePrivate;
31 Game::Game()
32 : current(-1)
33 , undo_pos(0) {
36 Game::~Game() {
39 void Game::onAdded(const Index&) {
42 void Game::onRemoved(const Index&) {
45 void Game::onEntryChanged(const Index&, int) {
48 void Game::onPromoteVariation(const Index&, int) {
51 void Game::onSetComment(const Index&, const QString&) {
54 void Game::onSetVComment(const Index&, int, const QString&) {
57 void Game::onCurrentIndexChanged(const Index&) {
60 void Game::onAvailableUndo(bool) {
63 void Game::onAvailableRedo(bool) {
66 Entry* Game::fetch(const Index& ix) {
67 int at;
68 History *vec = fetchRef(ix, &at);
69 return vec ? &(*vec)[at] : NULL;
72 const Entry* Game::fetch(const Index& ix) const {
73 int at;
74 const History *vec = fetchRef(ix, &at);
75 return vec ? &(*vec)[at] : NULL;
78 History* Game::fetchRef(const Index& ix, int* idx) {
79 if(ix.num_moves >= (int)history.size() || ix.num_moves < 0 )
80 return NULL;
82 History* aretv = &history;
83 Entry* retv = &history[ix.num_moves];
84 if(idx) *idx = ix.num_moves;
86 for(int i=0; i<(int)ix.nested.size();i++) {
87 Variations::iterator it = retv->variations.find(ix.nested[i].variation);
88 if(it == retv->variations.end() || ix.nested[i].num_moves >= (int)it->second.size()
89 || ix.nested[i].num_moves < 0 )
90 return NULL;
92 aretv = &it->second;
93 retv = &it->second[ix.nested[i].num_moves];
94 if(idx) *idx = ix.nested[i].num_moves;
96 return aretv;
99 const History* Game::fetchRef(const Index& ix, int* idx) const {
100 return const_cast<const History*>(const_cast<Game*>(this)->fetchRef(ix, idx));
103 void Game::testMove(const Index& ix) {
104 if (ix != Index(0)) {
105 Entry *e1 = fetch(ix.prev());
106 Entry *e2 = fetch(ix);
107 if(!e1 || !e2 || !e1->position || !e2->move)
108 return;
110 if (!e1->position->testMove(e2->move))
111 ERROR("invalid move added to game history!");
115 void Game::testMove() {
116 testMove(current);
119 void Game::saveUndo(const UndoOp& op) {
120 bool redo = undo_pos < (int)undo_history.size();
122 while(undo_pos < (int)undo_history.size())
123 undo_history.pop_back();
124 undo_history.push_back(op);
125 undo_pos++;
127 if(undo_pos == 1)
128 onAvailableUndo(true);
129 if(redo)
130 onAvailableRedo(false);
134 Index Game::index() const {
135 return current;
138 Index Game::lastMainlineIndex() const {
139 return Index(history.size()-1);
142 bool Game::containsIndex(const Index& index) const {
143 return !!fetch(index);
146 MovePtr Game::move() const {
147 return move(current);
150 MovePtr Game::move(const Index& index) const {
151 Entry *e = (Entry*)fetch(index);
152 if(!e) {
153 ERROR("Index out of range!");
154 return MovePtr();
156 return e->move;
159 PositionPtr Game::position() const {
160 return position(current);
163 PositionPtr Game::position(const Index& index) const {
164 Entry *e = (Entry*)fetch(index);
165 if(!e) {
166 ERROR("Index " << index << " out of range!");
167 return PositionPtr();
169 return e->position;
172 QString Game::comment() const {
173 return comment(current);
176 QString Game::comment(const Index& index) const {
177 const Entry *e = fetch(index);
178 if(!e) {
179 ERROR("Index out of range!");
180 return QString();
182 return e->comment;
185 void Game::reset(PositionPtr pos) {
186 Q_ASSERT(pos);
188 undo_pos = 0;
189 undo_history.clear();
190 history.clear();
191 history.push_back( Entry(MovePtr(), pos) );
192 current = Index(0);
193 onCurrentIndexChanged();
196 void Game::undo() {
197 if(undo_pos <= 0) {
198 ERROR("Cannot undo at the beginning of the undo history!");
199 return;
202 bool last_undo = undo_pos == 1;
203 bool now_redo = undo_pos == (int)undo_history.size();
205 undo_pos--;
206 UndoOp* op = &(undo_history[undo_pos]);
208 if(boost::get<UndoAdd>(op)) {
209 UndoAdd *a = boost::get<UndoAdd>(op);
211 if(a->index.atVariationStart() ) {
212 Entry* e = fetch(a->index.prev());
213 Q_ASSERT(e);
215 int v = a->index.nested[a->index.nested.size()-1].variation;
216 Q_ASSERT(e->variations.count(v) == 1);
217 Q_ASSERT(e->variations[v].size() == 1);
219 e->variations.erase(v);
221 else {
222 int at;
223 std::vector<Entry>* vec = fetchRef(a->index, &at);
224 Q_ASSERT(vec);
225 Q_ASSERT((int)vec->size() == at+1);
227 vec->pop_back();
230 if(current == a->index) {
231 current = current.prev();
232 onCurrentIndexChanged();
235 onRemoved(a->index);
237 else if(boost::get<UndoPromote>(op)) {
238 UndoPromote *p = boost::get<UndoPromote>(op);
240 int at;
241 std::vector<Entry>* vec = fetchRef(p->index, &at);
242 Q_ASSERT(vec);
243 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
245 History vold = (*vec)[at].variations[p->variation];
246 History vnew;
247 for(int i=at+1; i<(int)vec->size(); i++)
248 vnew.push_back((*vec)[i]);
249 while((int)vec->size()>at+1)
250 vec->pop_back();
251 for(int i=0; i<(int)vold.size(); i++)
252 vec->push_back(vold[i]);
253 (*vec)[at].variations[p->variation] = vnew;
255 current = current.flipVariation(p->index, p->variation);
256 onPromoteVariation(p->index, p->variation);
257 //onCurrentIndexChanged();
259 else if(boost::get<UndoTruncate>(op)) {
260 UndoTruncate *t = boost::get<UndoTruncate>(op);
262 int at;
263 std::vector<Entry>* vec = fetchRef(t->index, &at);
264 Q_ASSERT(vec);
265 Q_ASSERT((int)vec->size() == at+1);
266 Q_ASSERT((*vec)[at].variations.empty());
268 for(int i=0;i<(int)t->history.size();i++)
269 vec->push_back(t->history[i]);
270 (*vec)[at].variations = t->variations;
271 (*vec)[at].vcomments = t->vcomments;
273 if(t->history.size())
274 onAdded(t->index.next());
275 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
276 onAdded(t->index.next(it->first));
277 for(VComments::iterator it = t->vcomments.begin(); it != t->vcomments.end(); ++it)
278 onSetVComment(t->index, it->first, it->second);
280 else if(boost::get<UndoRemove>(op)) {
281 UndoRemove *r = boost::get<UndoRemove>(op);
283 Entry *e = fetch(r->index);
284 e->variations[r->variation] = r->history;
285 onAdded(r->index.next(r->variation));
286 if(!r->vcomment.isEmpty()) {
287 e->vcomments[r->variation] = r->vcomment;
288 onSetVComment(r->index, r->variation, r->vcomment);
291 else if(boost::get<UndoClear>(op)) {
292 UndoClear *c = boost::get<UndoClear>(op);
294 Entry *e = fetch(c->index);
295 e->variations = c->variations;
296 e->vcomments = c->vcomments;
297 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
298 onAdded(c->index.next(it->first));
299 for(VComments::iterator it = c->vcomments.begin(); it != c->vcomments.end(); ++it)
300 onSetVComment(c->index, it->first, it->second);
302 else if(boost::get<UndoSetComment>(op)) {
303 UndoSetComment *sc = boost::get<UndoSetComment>(op);
304 Entry *e = fetch(sc->index);
305 Q_ASSERT(e);
307 if(sc->variation == -1) {
308 e->comment = sc->old_comment;
309 onSetComment(sc->index, sc->old_comment);
311 else {
312 if(sc->old_comment.isEmpty())
313 e->vcomments.erase(sc->variation);
314 else
315 e->vcomments[sc->variation] = sc->old_comment;
316 onSetVComment(sc->index, sc->variation, sc->old_comment);
320 if(last_undo)
321 onAvailableUndo(false);
322 if(now_redo)
323 onAvailableRedo(true);
326 void Game::redo() {
327 if(undo_pos >= (int)undo_history.size()) {
328 ERROR("Cannot redo at the end of the undo history!");
329 return;
332 bool now_undo = undo_pos == 0;
333 bool last_redo = undo_pos == (int)undo_history.size()-1;
335 UndoOp* op = &(undo_history[undo_pos]);
336 undo_pos++;
338 if(boost::get<UndoAdd>(op)) {
339 UndoAdd *a = boost::get<UndoAdd>(op);
341 if(a->index.atVariationStart() ) {
342 Entry* e = fetch(a->index.prev());
343 Q_ASSERT(e);
345 int v = a->index.nested[a->index.nested.size()-1].variation;
346 Q_ASSERT(e->variations.count(v) == 0);
348 History h;
349 h.push_back(a->entry);
350 e->variations[v] = h;
352 else {
353 int at;
354 std::vector<Entry>* vec = fetchRef(a->index.prev(), &at);
355 Q_ASSERT(vec);
356 Q_ASSERT((int)vec->size() == at+1);
358 vec->push_back(a->entry);
361 onAdded(a->index);
363 else if(boost::get<UndoPromote>(op)) {
364 UndoPromote *p = boost::get<UndoPromote>(op);
366 int at;
367 std::vector<Entry>* vec = fetchRef(p->index, &at);
369 Q_ASSERT(vec);
370 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
371 History vold = (*vec)[at].variations[p->variation];
372 History vnew;
373 for(int i=at+1; i<(int)vec->size(); i++)
374 vnew.push_back((*vec)[i]);
375 while((int)vec->size()>at+1)
376 vec->pop_back();
377 for(int i=0; i<(int)vold.size(); i++)
378 vec->push_back(vold[i]);
379 (*vec)[at].variations[p->variation] = vnew;
381 current = current.flipVariation(p->index, p->variation);
382 onPromoteVariation(p->index, p->variation);
383 //onCurrentIndexChanged();
385 else if(boost::get<UndoTruncate>(op)) {
386 UndoTruncate *t = boost::get<UndoTruncate>(op);
388 int at;
389 std::vector<Entry>* vec = fetchRef(t->index, &at);
390 Q_ASSERT(vec);
391 Q_ASSERT((int)vec->size() == at+1+(int)t->history.size());
393 while((int)vec->size() > at+1)
394 vec->pop_back();
395 (*vec)[at].variations.clear();
396 (*vec)[at].vcomments.clear();
398 if(current > t->index) {
399 current = t->index;
400 onCurrentIndexChanged();
403 if(t->history.size())
404 onRemoved(t->index.next());
405 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
406 onRemoved(t->index.next(it->first));
408 else if(boost::get<UndoRemove>(op)) {
409 UndoRemove *r = boost::get<UndoRemove>(op);
411 Entry *e = fetch(r->index);
412 e->variations.erase(r->variation);
413 e->vcomments.erase(r->variation);
414 onRemoved(r->index.next(r->variation));
416 else if(boost::get<UndoClear>(op)) {
417 UndoClear *c = boost::get<UndoClear>(op);
419 Entry *e = fetch(c->index);
420 e->variations.clear();
421 e->vcomments.clear();
422 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
423 onRemoved(c->index.next(it->first));
425 else if(boost::get<UndoSetComment>(op)) {
426 UndoSetComment *sc = boost::get<UndoSetComment>(op);
427 Entry *e = fetch(sc->index);
428 Q_ASSERT(e);
430 if(sc->variation == -1) {
431 e->comment = sc->new_comment;
432 onSetComment(sc->index, sc->new_comment);
434 else {
435 if(sc->new_comment.isEmpty())
436 e->vcomments.erase(sc->variation);
437 else
438 e->vcomments[sc->variation] = sc->new_comment;
439 onSetVComment(sc->index, sc->variation, sc->new_comment);
443 if(now_undo)
444 onAvailableUndo(true);
445 if(last_redo)
446 onAvailableRedo(false);
449 void Game::setComment(const QString& c) {
450 setComment(current, c);
453 void Game::setComment(const Index& ix, const QString& c) {
454 Entry* e = fetch(ix);
455 if(!e) {
456 ERROR("Invalid index!");
457 return;
459 if(e->comment == c)
460 return;
462 saveUndo(UndoSetComment(ix, -1, e->comment, c));
463 e->comment = c;
464 onSetComment(ix, c);
467 void Game::setVComment(const Index& ix, int v, const QString& c) {
468 Entry* e = fetch(ix);
469 if(!e) {
470 ERROR("Invalid index!");
471 return;
473 QString oc = e->vcomments.count(v) ? e->vcomments[v] : QString();
474 if(oc == c)
475 return;
477 saveUndo(UndoSetComment(ix, v, oc, c));
478 if(c.isEmpty())
479 e->vcomments.erase(v);
480 else
481 e->vcomments[v] = c;
482 onSetVComment(ix, v, c);
485 void Game::promoteVariation() {
486 promoteVariation(current);
489 void Game::promoteVariation(const Index& _ix) {
490 if(_ix.nested.size()==0) {
491 ERROR("Cannot promote main line!");
492 return;
494 Index ix = _ix;
495 int v = ix.nested[ix.nested.size()-1].variation;
496 ix.nested.pop_back();
498 promoteVariation(ix, v);
501 void Game::promoteVariation(const Index& ix, int v) {
502 int at;
503 std::vector<Entry>* vec = fetchRef(ix, &at);
504 Q_ASSERT(vec);
505 Q_ASSERT((*vec)[at].variations.count(v)==1);
507 History vold = (*vec)[at].variations[v];
508 History vnew;
509 for(int i=at+1; i<(int)vec->size(); i++)
510 vnew.push_back((*vec)[i]);
511 while((int)vec->size()>at+1)
512 vec->pop_back();
513 for(int i=0; i<(int)vold.size(); i++)
514 vec->push_back(vold[i]);
515 (*vec)[at].variations[v] = vnew;
517 saveUndo(UndoPromote(ix, v));
518 current = current.flipVariation(ix, v);
519 onPromoteVariation(ix, v);
520 //don't call onCurrentIndexChanged(), as the position did not change actually
523 void Game::removeVariation(int v) {
524 removeVariation(current, v);
527 void Game::removeVariation(const Index& _ix) {
528 if(_ix.nested.size()==0) {
529 ERROR("Cannot remove main line!");
530 return;
532 Index ix = _ix;
533 int v = ix.nested[ix.nested.size()-1].variation;
534 ix.nested.pop_back();
536 removeVariation(ix, v);
539 void Game::removeVariation(const Index& ix, int v) {
540 Entry* e = fetch(ix);
542 saveUndo(UndoRemove(ix, v, e->variations[v],
543 e->vcomments.count(v) ? e->vcomments[v] : QString() ));
544 e->variations.erase(v);
545 e->vcomments.erase(v);
547 onRemoved(ix.next(v));
548 if(current >= ix.next(v)) {
549 current = ix;
550 onCurrentIndexChanged();
554 void Game::clearVariations() {
555 clearVariations(current);
558 void Game::clearVariations(const Index& ix) {
559 Entry* e = fetch(ix);
561 UndoClear uc(ix, e->variations, e->vcomments);
562 saveUndo(uc);
563 e->variations.clear();
564 e->vcomments.clear();
566 for(Variations::iterator it = uc.variations.begin(); it != uc.variations.end(); ++it)
567 onRemoved(ix.next(it->first));
568 if(current > ix && !(current >= ix.next())) {
569 current = ix;
570 onCurrentIndexChanged();
574 void Game::truncate() {
575 truncate(current);
578 void Game::truncate(const Index& ix) {
579 int at;
580 History* vec = fetchRef(ix, &at);
581 if(!vec) {
582 ERROR("Truncating at an unexisting index!");
583 return;
586 Entry *e = &(*vec)[at];
587 UndoTruncate undo(ix);
588 for(int i=at+1; i<(int)vec->size();i++)
589 undo.history.push_back((*vec)[i]);
590 while((int)vec->size()>at+1)
591 vec->pop_back();
593 undo.variations = e->variations;
594 undo.vcomments = e->vcomments;
595 saveUndo(undo);
596 e->variations.clear();
597 e->vcomments.clear();
599 if(undo.history.size())
600 onRemoved(undo.index.next());
601 for(Variations::iterator it = undo.variations.begin(); it != undo.variations.end(); ++it)
602 onRemoved(undo.index.next(it->first));
604 if(current > ix) {
605 current = ix;
606 onCurrentIndexChanged();
610 void Game::add(MovePtr m, PositionPtr pos) {
611 Q_ASSERT(pos);
613 Index old_c = current;
614 int at;
615 std::vector<Entry>* vec = fetchRef(current, &at);
616 Q_ASSERT(vec);
618 /* add the move on the mainline */
619 if((int)vec->size() <= at+1 ) {
620 Q_ASSERT((int)vec->size() == at+1);
621 vec->push_back(Entry(m, pos));
622 current = current.next();
623 testMove();
624 saveUndo(UndoAdd(current, Entry(m, pos)));
625 onAdded(current);
626 onCurrentIndexChanged(old_c);
628 /* we are playing the move that is already next in the mainline */
629 else if( (*vec)[at+1].position && (*vec)[at+1].position->equals(pos)) {
630 current = current.next();
631 onCurrentIndexChanged(old_c);
632 /* no need to test the move */
634 else {
635 Entry *e = fetch(current);
636 Q_ASSERT(e);
638 /* check if a variations with this move already exists. */
639 for(Variations::iterator it = e->variations.begin(); it != e->variations.end(); ++it)
640 if(it->second.size() > 0 && it->second[0].position
641 && it->second[0].position->equals(pos) ) {
642 current = current.next(it->first);
643 onCurrentIndexChanged(old_c);
645 return;
648 int var_id = e->last_var_id++;
649 e->variations[var_id].push_back(Entry(m, pos));
650 current = current.next(var_id);
651 testMove();
652 saveUndo(UndoAdd(current, Entry(m, pos)));
653 onAdded(current);
654 onCurrentIndexChanged(old_c);
658 bool Game::insert(MovePtr m, PositionPtr pos, const Index& at) {
659 Entry *e = fetch(at);
661 if(!e) {
662 if(at.nested.size() == 0) {
663 if(undo_history.size()) {
664 undo_pos = 0;
665 undo_history.clear();
667 int hs = history.size();
668 history.resize(at.num_moves + 1);
669 history[at.num_moves] = Entry(m, pos);
670 testMove(at);
671 onAdded(Index(hs));
672 return true;
674 else {
675 ERROR("Index out if range!");
676 return false;
680 if(undo_history.size()) {
681 undo_pos = 0;
682 undo_history.clear();
684 bool res = e->position && e->position->equals(pos);
685 e->move = m;
686 e->position = pos;
687 testMove(at);
688 testMove(at.next());
689 for (Variations::const_iterator it = e->variations.begin();
690 it != e->variations.end(); ++it)
691 testMove(at.next(it->first));
692 onEntryChanged(at);
693 return res;
696 bool Game::lastPosition() const {
697 return !fetch(current.next());
700 bool Game::back() {
701 if (current <= 0) return false; // first entry or uninitialized
702 Index old_c = current;
703 Index new_c = current.prev();
705 Entry *e = fetch(new_c);
706 if(!e || e->position == 0) return false; // gap immediately before current
707 current = new_c;
708 onCurrentIndexChanged(old_c);
710 return true;
713 bool Game::forward() {
714 Index old_c = current;
715 Index new_c = current.next();
717 Entry *e = fetch(new_c);
718 if(!e || e->position == 0) {
719 return false; // gap immediately before current
721 current = new_c;
722 onCurrentIndexChanged(old_c);
724 return true;
727 void Game::gotoFirst() {
728 Index old_c = current;
729 current = Index(0);
730 onCurrentIndexChanged(old_c);
733 void Game::gotoLast() {
734 int at;
735 std::vector<Entry>* vec = fetchRef(current, &at);
736 Q_ASSERT(vec);
737 Q_ASSERT((int)vec->size() > at);
739 if((int)vec->size() > at+1) {
740 Index old_c = current;
741 current = current.next(-1, vec->size()-1-at);
742 onCurrentIndexChanged(old_c);
746 bool Game::goTo(const Index& index) {
747 if (fetch(index)) {
748 Index old_c = current;
749 current = index;
750 onCurrentIndexChanged(old_c);
751 return true;
753 return false;
756 QString Game::variationPgn(const History& vec, const Entry& e,
757 int start, const Index& _ix) const {
758 Index ix = _ix;
759 QString res;
761 for (int i = start; i < static_cast<int>(vec.size()); i++) {
762 const Entry& preve = (i > start) ? vec[i-1] : e;
764 QString mv = (vec[i].move && preve.position) ?
765 vec[i].move->toString("compact", preve.position ) : "???";
766 #if 0
767 if (ix == current)
768 mv = "[[" + mv + "]]";
769 #endif
771 int n = ix.totalNumMoves()+1;
772 if(i==start || n%2==0)
773 mv = QString::number(n/2)+(n%2==1 ? ". ... " : ". ") + mv;
774 if (i > start)
775 mv = " " + mv;
777 res += mv;
779 if(!vec[i].comment.isEmpty())
780 res += " {" + vec[i].comment + "}";
782 if(i > 0) {
783 for(Variations::const_iterator it = vec[i-1].variations.begin();
784 it != vec[i-1].variations.end(); ++it) {
785 res += " (";
786 if(vec[i-1].vcomments.count(it->first))
787 res += "{" + vec[i-1].vcomments.find(it->first)->second + "} ";
788 res += variationPgn(it->second, vec[i - 1], 0,
789 ix.prev().next(it->first)) + ")";
793 ix = ix.next();
795 return res;
798 QString Game::pgn() const {
799 return variationPgn(history, history[0], 1, Index(1));
802 void Game::load(const PGN& pgn) {
803 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
804 VariantPtr vi;
806 if (var == pgn.m_tags.end()) {
807 vi = Variants::instance().get("chess");
809 else if (!(vi = Variants::instance().get(var->second))) {
810 ERROR("No such variant " << var->second);
811 return;
814 std::map<QString, QString>::const_iterator fen = pgn.m_tags.find("FEN");
815 PositionPtr pos;
817 if(var == pgn.m_tags.end()) {
818 pos = vi->createPosition();
819 pos->setup();
821 #if 0 // BROKEN
822 else if( !(pos = vi->createPositionFromFEN(fen->second))) {
823 ERROR("Wrong fen " << fen->second);
824 return;
826 #endif
828 //TODO: what about options? FEN rules?
830 load(pos, pgn);
833 void Game::load(PositionPtr pos, const PGN& pgn) {
834 current = Index(0);
835 undo_history.clear();
836 undo_pos = 0;
838 if(history.size()) {
839 Entry* fe = &history[0];
840 int old_history_size = history.size();
841 std::vector<int> v_ids;
843 while(history.size()>1)
844 history.pop_back();
845 for(Variations::const_iterator it = fe->variations.begin();
846 it != fe->variations.end(); ++it)
847 v_ids.push_back(it->first);
848 fe->variations.clear();
849 fe->vcomments.clear();
851 for(int i=0;i<(int)v_ids.size();i++)
852 onRemoved(Index(0).next(v_ids[i]));
853 if(old_history_size>1)
854 onRemoved(Index(1));
855 v_ids.clear();
856 history[0].position = pos;
858 else
859 history.push_back( Entry(MovePtr(), pos) );
861 QString vcomment;
862 std::vector<Index> var_stack;
863 bool var_start = false;
865 for (uint i = 0; i < pgn.m_entries.size(); i++) {
866 if(boost::get<QString>(pgn[i])) {
867 if(var_start)
868 vcomment += *boost::get<QString>(pgn[i]);
869 else {
870 Entry *e = fetch(current);
871 Q_ASSERT(e);
873 e->comment += *boost::get<QString>(pgn[i]);
876 else if(boost::get<PGN::BeginVariation>(pgn[i])) {
877 var_stack.push_back(current);
878 var_start = true;
880 else if(boost::get<PGN::EndVariation>(pgn[i])) {
881 if(var_stack.size() == 0) {
882 ERROR("Unexpected end variation!");
883 break;
885 current = var_stack[var_stack.size()-1];
886 var_stack.pop_back();
888 else if(boost::get<PGN::Move>(pgn[i])) {
889 const PGN::Move *pm = boost::get<PGN::Move>(pgn[i]);
891 int n = current.totalNumMoves()+1;
892 if(var_start) {
893 if(!pm->m_number)
894 current = current.prev();
895 else if(pm->m_number>n+1)
896 ERROR("Too far variation!");
897 else {
898 if(pm->m_number<n)
899 ERROR("Too near variation!");
900 current = current.prev(n + 1 - pm->m_number);
903 else if(pm->m_number && pm->m_number!=n+1)
904 ERROR("Move number mismatch!");
906 PositionPtr pos = position();
907 MovePtr m = pos->getMove(pm->m_move);
909 if(!m || !pos->testMove(m))
910 break;
912 PositionPtr newPos = pos->clone();
913 newPos->move(m);
915 int at;
916 History *vec = fetchRef(current, &at);
917 Q_ASSERT(vec);
919 if(var_start) {
920 Entry *e = &(*vec)[at];
921 int var_id = e->last_var_id++;
922 e->variations[var_id].push_back(Entry(m, newPos));
923 if(!vcomment.isEmpty()) {
924 e->vcomments[var_id] = vcomment;
925 vcomment = QString();
927 /* this is a hack, but the mainline should NEVER
928 be empty if there is a variation*/
929 if((int)vec->size() - 1 == at)
930 vec->push_back(Entry(m, newPos));
932 current = current.next(var_id);
934 else {
935 if((int)vec->size() - 1 == at)
936 vec->push_back(Entry(m, newPos));
937 else
938 (*vec)[at] = Entry(m, newPos);
940 current = current.next();
943 var_start = false;
947 if(history.size()>1)
948 onAdded(Index(1));
949 Entry* e = fetch(Index(0));
950 for(Variations::const_iterator it = e->variations.begin();
951 it != e->variations.end(); ++it)
952 onAdded(Index(0).next(it->first));
953 for(VComments::const_iterator it = e->vcomments.begin();
954 it != e->vcomments.end(); ++it)
955 onSetVComment(Index(0), it->first, it->second);
957 current = Index(0);
958 onCurrentIndexChanged();