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.
14 #pragma warning( push )
15 #pragma warning( disable : 4100 )
16 #include <boost/variant.hpp>
17 #pragma warning( pop )
19 #include <boost/variant.hpp>
22 #include "pgnparser.h"
28 using namespace GamePrivate
;
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
) {
68 History
*vec
= fetchRef(ix
, &at
);
69 return vec
? &(*vec
)[at
] : NULL
;
72 const Entry
* Game::fetch(const Index
& ix
) const {
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 )
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 )
93 retv
= &it
->second
[ix
.nested
[i
].num_moves
];
94 if(idx
) *idx
= ix
.nested
[i
].num_moves
;
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
)
110 if (!e1
->position
->testMove(e2
->move
))
111 ERROR("invalid move added to game history!");
115 void Game::testMove() {
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
);
128 onAvailableUndo(true);
130 onAvailableRedo(false);
134 Index
Game::index() const {
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
);
153 ERROR("Index out of range!");
159 PositionPtr
Game::position() const {
160 return position(current
);
163 PositionPtr
Game::position(const Index
& index
) const {
164 Entry
*e
= (Entry
*)fetch(index
);
166 ERROR("Index " << index
<< " out of range!");
167 return PositionPtr();
172 QString
Game::comment() const {
173 return comment(current
);
176 QString
Game::comment(const Index
& index
) const {
177 const Entry
*e
= fetch(index
);
179 ERROR("Index out of range!");
185 void Game::reset(PositionPtr pos
) {
189 undo_history
.clear();
191 history
.push_back( Entry(MovePtr(), pos
) );
193 onCurrentIndexChanged();
198 ERROR("Cannot undo at the beginning of the undo history!");
202 bool last_undo
= undo_pos
== 1;
203 bool now_redo
= undo_pos
== (int)undo_history
.size();
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());
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
);
223 std::vector
<Entry
>* vec
= fetchRef(a
->index
, &at
);
225 Q_ASSERT((int)vec
->size() == at
+1);
230 if(current
== a
->index
) {
231 current
= current
.prev();
232 onCurrentIndexChanged();
237 else if(boost::get
<UndoPromote
>(op
)) {
238 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
241 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
243 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
245 History vold
= (*vec
)[at
].variations
[p
->variation
];
247 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
248 vnew
.push_back((*vec
)[i
]);
249 while((int)vec
->size()>at
+1)
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
);
263 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
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
);
307 if(sc
->variation
== -1) {
308 e
->comment
= sc
->old_comment
;
309 onSetComment(sc
->index
, sc
->old_comment
);
312 if(sc
->old_comment
.isEmpty())
313 e
->vcomments
.erase(sc
->variation
);
315 e
->vcomments
[sc
->variation
] = sc
->old_comment
;
316 onSetVComment(sc
->index
, sc
->variation
, sc
->old_comment
);
321 onAvailableUndo(false);
323 onAvailableRedo(true);
327 if(undo_pos
>= (int)undo_history
.size()) {
328 ERROR("Cannot redo at the end of the undo history!");
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
]);
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());
345 int v
= a
->index
.nested
[a
->index
.nested
.size()-1].variation
;
346 Q_ASSERT(e
->variations
.count(v
) == 0);
349 h
.push_back(a
->entry
);
350 e
->variations
[v
] = h
;
354 std::vector
<Entry
>* vec
= fetchRef(a
->index
.prev(), &at
);
356 Q_ASSERT((int)vec
->size() == at
+1);
358 vec
->push_back(a
->entry
);
363 else if(boost::get
<UndoPromote
>(op
)) {
364 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
367 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
370 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
371 History vold
= (*vec
)[at
].variations
[p
->variation
];
373 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
374 vnew
.push_back((*vec
)[i
]);
375 while((int)vec
->size()>at
+1)
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
);
389 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
391 Q_ASSERT((int)vec
->size() == at
+1+(int)t
->history
.size());
393 while((int)vec
->size() > at
+1)
395 (*vec
)[at
].variations
.clear();
396 (*vec
)[at
].vcomments
.clear();
398 if(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
);
430 if(sc
->variation
== -1) {
431 e
->comment
= sc
->new_comment
;
432 onSetComment(sc
->index
, sc
->new_comment
);
435 if(sc
->new_comment
.isEmpty())
436 e
->vcomments
.erase(sc
->variation
);
438 e
->vcomments
[sc
->variation
] = sc
->new_comment
;
439 onSetVComment(sc
->index
, sc
->variation
, sc
->new_comment
);
444 onAvailableUndo(true);
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
);
456 ERROR("Invalid index!");
462 saveUndo(UndoSetComment(ix
, -1, e
->comment
, c
));
467 void Game::setVComment(const Index
& ix
, int v
, const QString
& c
) {
468 Entry
* e
= fetch(ix
);
470 ERROR("Invalid index!");
473 QString oc
= e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString();
477 saveUndo(UndoSetComment(ix
, v
, oc
, c
));
479 e
->vcomments
.erase(v
);
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!");
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
) {
503 std::vector
<Entry
>* vec
= fetchRef(ix
, &at
);
505 Q_ASSERT((*vec
)[at
].variations
.count(v
)==1);
507 History vold
= (*vec
)[at
].variations
[v
];
509 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
510 vnew
.push_back((*vec
)[i
]);
511 while((int)vec
->size()>at
+1)
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!");
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
)) {
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
);
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())) {
570 onCurrentIndexChanged();
574 void Game::truncate() {
578 void Game::truncate(const Index
& ix
) {
580 History
* vec
= fetchRef(ix
, &at
);
582 ERROR("Truncating at an unexisting index!");
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)
593 undo
.variations
= e
->variations
;
594 undo
.vcomments
= e
->vcomments
;
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
));
606 onCurrentIndexChanged();
610 void Game::add(MovePtr m
, PositionPtr pos
) {
613 Index old_c
= current
;
615 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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();
624 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
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 */
635 Entry
*e
= fetch(current
);
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
);
648 int var_id
= e
->last_var_id
++;
649 e
->variations
[var_id
].push_back(Entry(m
, pos
));
650 current
= current
.next(var_id
);
652 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
654 onCurrentIndexChanged(old_c
);
658 bool Game::insert(MovePtr m
, PositionPtr pos
, const Index
& at
) {
659 Entry
*e
= fetch(at
);
662 if(at
.nested
.size() == 0) {
663 if(undo_history
.size()) {
665 undo_history
.clear();
667 int hs
= history
.size();
668 history
.resize(at
.num_moves
+ 1);
669 history
[at
.num_moves
] = Entry(m
, pos
);
675 ERROR("Index out if range!");
680 if(undo_history
.size()) {
682 undo_history
.clear();
684 bool res
= e
->position
&& e
->position
->equals(pos
);
689 for (Variations::const_iterator it
= e
->variations
.begin();
690 it
!= e
->variations
.end(); ++it
)
691 testMove(at
.next(it
->first
));
696 bool Game::lastPosition() const {
697 return !fetch(current
.next());
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
708 onCurrentIndexChanged(old_c
);
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
722 onCurrentIndexChanged(old_c
);
727 void Game::gotoFirst() {
728 Index old_c
= current
;
730 onCurrentIndexChanged(old_c
);
733 void Game::gotoLast() {
735 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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
) {
748 Index old_c
= current
;
750 onCurrentIndexChanged(old_c
);
756 QString
Game::variationPgn(const History
& vec
, const Entry
& e
,
757 int start
, const Index
& _ix
) const {
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
) : "???";
768 mv
= "[[" + mv
+ "]]";
771 int n
= ix
.totalNumMoves()+1;
772 if(i
==start
|| n
%2==0)
773 mv
= QString::number(n
/2)+(n
%2==1 ? ". ... " : ". ") + mv
;
779 if(!vec
[i
].comment
.isEmpty())
780 res
+= " {" + vec
[i
].comment
+ "}";
783 for(Variations::const_iterator it
= vec
[i
-1].variations
.begin();
784 it
!= vec
[i
-1].variations
.end(); ++it
) {
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
)) + ")";
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");
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
);
814 std::map
<QString
, QString
>::const_iterator fen
= pgn
.m_tags
.find("FEN");
817 if(var
== pgn
.m_tags
.end()) {
818 pos
= vi
->createPosition();
822 else if( !(pos
= vi
->createPositionFromFEN(fen
->second
))) {
823 ERROR("Wrong fen " << fen
->second
);
828 //TODO: what about options? FEN rules?
833 void Game::load(PositionPtr pos
, const PGN
& pgn
) {
835 undo_history
.clear();
839 Entry
* fe
= &history
[0];
840 int old_history_size
= history
.size();
841 std::vector
<int> v_ids
;
843 while(history
.size()>1)
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)
856 history
[0].position
= pos
;
859 history
.push_back( Entry(MovePtr(), pos
) );
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
])) {
868 vcomment
+= *boost::get
<QString
>(pgn
[i
]);
870 Entry
*e
= fetch(current
);
873 e
->comment
+= *boost::get
<QString
>(pgn
[i
]);
876 else if(boost::get
<PGN::BeginVariation
>(pgn
[i
])) {
877 var_stack
.push_back(current
);
880 else if(boost::get
<PGN::EndVariation
>(pgn
[i
])) {
881 if(var_stack
.size() == 0) {
882 ERROR("Unexpected end variation!");
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;
894 current
= current
.prev();
895 else if(pm
->m_number
>n
+1)
896 ERROR("Too far variation!");
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
))
912 PositionPtr newPos
= pos
->clone();
916 History
*vec
= fetchRef(current
, &at
);
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
);
935 if((int)vec
->size() - 1 == at
)
936 vec
->push_back(Entry(m
, newPos
));
938 (*vec
)[at
] = Entry(m
, newPos
);
940 current
= current
.next();
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
);
958 onCurrentIndexChanged();