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>
23 #include "pgnparser.h"
30 using namespace GamePrivate
;
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
) {
70 History
*vec
= fetchRef(ix
, &at
);
71 return vec
? &(*vec
)[at
] : NULL
;
74 const Entry
* Game::fetch(const Index
& ix
) const {
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 )
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 )
95 retv
= &it
->second
[ix
.nested
[i
].num_moves
];
96 if(idx
) *idx
= ix
.nested
[i
].num_moves
;
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
)
112 if (!e1
->position
->testMove(e2
->move
))
113 ERROR("invalid move added to game history!");
117 void Game::testMove() {
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
);
130 onAvailableUndo(true);
132 onAvailableRedo(false);
136 Index
Game::index() const {
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
);
155 ERROR("Index out of range!");
161 PositionPtr
Game::position() const {
162 return position(current
);
165 PositionPtr
Game::position(const Index
& index
) const {
166 Entry
*e
= (Entry
*)fetch(index
);
168 ERROR("Index " << index
<< " out of range!");
169 return PositionPtr();
174 QString
Game::comment() const {
175 return comment(current
);
178 QString
Game::comment(const Index
& index
) const {
179 const Entry
*e
= fetch(index
);
181 ERROR("Index out of range!");
187 void Game::reset(PositionPtr pos
) {
191 undo_history
.clear();
193 history
.push_back( Entry(MovePtr(), pos
) );
195 onCurrentIndexChanged();
200 ERROR("Cannot undo at the beginning of the undo history!");
204 bool last_undo
= undo_pos
== 1;
205 bool now_redo
= undo_pos
== (int)undo_history
.size();
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());
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
);
225 std::vector
<Entry
>* vec
= fetchRef(a
->index
, &at
);
227 Q_ASSERT((int)vec
->size() == at
+1);
232 if(current
== a
->index
) {
233 current
= current
.prev();
234 onCurrentIndexChanged();
239 else if(boost::get
<UndoPromote
>(op
)) {
240 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
243 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
245 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
247 History vold
= (*vec
)[at
].variations
[p
->variation
];
249 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
250 vnew
.push_back((*vec
)[i
]);
251 while((int)vec
->size()>at
+1)
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
);
265 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
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
);
309 if(sc
->variation
== -1) {
310 e
->comment
= sc
->old_comment
;
311 onSetComment(sc
->index
, sc
->old_comment
);
314 if(sc
->old_comment
.isEmpty())
315 e
->vcomments
.erase(sc
->variation
);
317 e
->vcomments
[sc
->variation
] = sc
->old_comment
;
318 onSetVComment(sc
->index
, sc
->variation
, sc
->old_comment
);
323 onAvailableUndo(false);
325 onAvailableRedo(true);
329 if(undo_pos
>= (int)undo_history
.size()) {
330 ERROR("Cannot redo at the end of the undo history!");
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
]);
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());
347 int v
= a
->index
.nested
[a
->index
.nested
.size()-1].variation
;
348 Q_ASSERT(e
->variations
.count(v
) == 0);
351 h
.push_back(a
->entry
);
352 e
->variations
[v
] = h
;
356 std::vector
<Entry
>* vec
= fetchRef(a
->index
.prev(), &at
);
358 Q_ASSERT((int)vec
->size() == at
+1);
360 vec
->push_back(a
->entry
);
365 else if(boost::get
<UndoPromote
>(op
)) {
366 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
369 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
372 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
373 History vold
= (*vec
)[at
].variations
[p
->variation
];
375 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
376 vnew
.push_back((*vec
)[i
]);
377 while((int)vec
->size()>at
+1)
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
);
391 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
393 Q_ASSERT((int)vec
->size() == at
+1+(int)t
->history
.size());
395 while((int)vec
->size() > at
+1)
397 (*vec
)[at
].variations
.clear();
398 (*vec
)[at
].vcomments
.clear();
400 if(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
);
432 if(sc
->variation
== -1) {
433 e
->comment
= sc
->new_comment
;
434 onSetComment(sc
->index
, sc
->new_comment
);
437 if(sc
->new_comment
.isEmpty())
438 e
->vcomments
.erase(sc
->variation
);
440 e
->vcomments
[sc
->variation
] = sc
->new_comment
;
441 onSetVComment(sc
->index
, sc
->variation
, sc
->new_comment
);
446 onAvailableUndo(true);
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
);
458 ERROR("Invalid index!");
464 saveUndo(UndoSetComment(ix
, -1, e
->comment
, c
));
469 void Game::setVComment(const Index
& ix
, int v
, const QString
& c
) {
470 Entry
* e
= fetch(ix
);
472 ERROR("Invalid index!");
475 QString oc
= e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString();
479 saveUndo(UndoSetComment(ix
, v
, oc
, c
));
481 e
->vcomments
.erase(v
);
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!");
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
) {
505 std::vector
<Entry
>* vec
= fetchRef(ix
, &at
);
507 Q_ASSERT((*vec
)[at
].variations
.count(v
)==1);
509 History vold
= (*vec
)[at
].variations
[v
];
511 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
512 vnew
.push_back((*vec
)[i
]);
513 while((int)vec
->size()>at
+1)
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!");
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
)) {
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
);
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())) {
572 onCurrentIndexChanged();
576 void Game::truncate() {
580 void Game::truncate(const Index
& ix
) {
582 History
* vec
= fetchRef(ix
, &at
);
584 ERROR("Truncating at an unexisting index!");
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)
595 undo
.variations
= e
->variations
;
596 undo
.vcomments
= e
->vcomments
;
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
));
608 onCurrentIndexChanged();
612 void Game::add(MovePtr m
, PositionPtr pos
) {
615 Index old_c
= current
;
617 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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();
626 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
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 */
637 Entry
*e
= fetch(current
);
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
);
650 int var_id
= e
->last_var_id
++;
651 e
->variations
[var_id
].push_back(Entry(m
, pos
));
652 current
= current
.next(var_id
);
654 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
656 onCurrentIndexChanged(old_c
);
660 bool Game::insert(MovePtr m
, PositionPtr pos
, const Index
& at
) {
661 Entry
*e
= fetch(at
);
664 if(at
.nested
.size() == 0) {
665 if(undo_history
.size()) {
667 undo_history
.clear();
669 int hs
= history
.size();
670 history
.resize(at
.num_moves
+ 1);
671 history
[at
.num_moves
] = Entry(m
, pos
);
677 ERROR("Index out if range!");
682 if(undo_history
.size()) {
684 undo_history
.clear();
686 bool res
= e
->position
&& e
->position
->equals(pos
);
691 for (Variations::const_iterator it
= e
->variations
.begin();
692 it
!= e
->variations
.end(); ++it
)
693 testMove(at
.next(it
->first
));
698 bool Game::lastPosition() const {
699 return !fetch(current
.next());
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
710 onCurrentIndexChanged(old_c
);
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
724 onCurrentIndexChanged(old_c
);
729 void Game::gotoFirst() {
730 Index old_c
= current
;
732 onCurrentIndexChanged(old_c
);
735 void Game::gotoLast() {
737 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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
) {
750 Index old_c
= current
;
752 onCurrentIndexChanged(old_c
);
758 QString
Game::variationPgn(const History
& vec
, const Entry
& e
,
759 int start
, const Index
& _ix
) const {
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
->SAN( preve
.position
) : "???";
770 mv
= "[[" + mv
+ "]]";
773 int n
= ix
.totalNumMoves()+1;
774 if(i
==start
|| n
%2==0)
775 mv
= QString::number(n
/2)+(n
%2==1 ? ". ... " : ". ") + mv
;
781 if(!vec
[i
].comment
.isEmpty())
782 res
+= " {" + vec
[i
].comment
+ "}";
785 for(Variations::const_iterator it
= vec
[i
-1].variations
.begin();
786 it
!= vec
[i
-1].variations
.end(); ++it
) {
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
)) + ")";
800 QString
Game::pgn() const {
801 return variationPgn(history
, history
[0], 1, Index(1));
805 void Game::load(const PGN
& pgn
) {
806 std::map
<QString
, QString
>::const_iterator var
= pgn
.m_tags
.find("Variant");
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
);
817 std::map
<QString
, QString
>::const_iterator fen
= pgn
.m_tags
.find("FEN");
820 if(var
== pgn
.m_tags
.end()) {
821 pos
= vi
->createPosition();
824 else if( !(pos
= vi
->createPositionFromFEN(fen
->second
))) {
825 ERROR("Wrong fen " << fen
->second
);
829 //TODO: what about options? FEN rules?
834 void Game::load(PositionPtr pos
, const PGN
& pgn
) {
836 undo_history
.clear();
840 Entry
* fe
= &history
[0];
841 int old_history_size
= history
.size();
842 std::vector
<int> v_ids
;
844 while(history
.size()>1)
846 for(Variations::const_iterator it
= fe
->variations
.begin();
847 it
!= fe
->variations
.end(); ++it
)
848 v_ids
.push_back(it
->first
);
849 fe
->variations
.clear();
850 fe
->vcomments
.clear();
852 for(int i
=0;i
<(int)v_ids
.size();i
++)
853 onRemoved(Index(0).next(v_ids
[i
]));
854 if(old_history_size
>1)
857 history
[0].position
= pos
;
860 history
.push_back( Entry(MovePtr(), pos
) );
863 std::vector
<Index
> var_stack
;
864 bool var_start
= false;
866 for (uint i
= 0; i
< pgn
.m_entries
.size(); i
++) {
867 if(boost::get
<QString
>(pgn
[i
])) {
869 vcomment
+= *boost::get
<QString
>(pgn
[i
]);
871 Entry
*e
= fetch(current
);
874 e
->comment
+= *boost::get
<QString
>(pgn
[i
]);
877 else if(boost::get
<PGN::BeginVariation
>(pgn
[i
])) {
878 var_stack
.push_back(current
);
881 else if(boost::get
<PGN::EndVariation
>(pgn
[i
])) {
882 if(var_stack
.size() == 0) {
883 ERROR("Unexpected end variation!");
886 current
= var_stack
[var_stack
.size()-1];
887 var_stack
.pop_back();
889 else if(boost::get
<PGN::Move
>(pgn
[i
])) {
890 const PGN::Move
*pm
= boost::get
<PGN::Move
>(pgn
[i
]);
892 int n
= current
.totalNumMoves()+1;
895 current
= current
.prev();
896 else if(pm
->m_number
>n
+1)
897 ERROR("Too far variation!");
900 ERROR("Too near variation!");
901 current
= current
.prev(n
+ 1 - pm
->m_number
);
904 else if(pm
->m_number
&& pm
->m_number
!=n
+1)
905 ERROR("Move number mismatch!");
907 PositionPtr pos
= position();
908 MovePtr m
= pos
->getMove(pm
->m_move
);
910 if(!m
|| !pos
->testMove(m
))
913 PositionPtr newPos
= pos
->clone();
917 History
*vec
= fetchRef(current
, &at
);
921 Entry
*e
= &(*vec
)[at
];
922 int var_id
= e
->last_var_id
++;
923 e
->variations
[var_id
].push_back(Entry(m
, newPos
));
924 if(!vcomment
.isEmpty()) {
925 e
->vcomments
[var_id
] = vcomment
;
926 vcomment
= QString();
928 /* this is a hack, but the mainline should NEVER
929 be empty if there is a variation*/
930 if((int)vec
->size() - 1 == at
)
931 vec
->push_back(Entry(m
, newPos
));
933 current
= current
.next(var_id
);
936 if((int)vec
->size() - 1 == at
)
937 vec
->push_back(Entry(m
, newPos
));
939 (*vec
)[at
] = Entry(m
, newPos
);
941 current
= current
.next();
950 Entry
* e
= fetch(Index(0));
951 for(Variations::const_iterator it
= e
->variations
.begin();
952 it
!= e
->variations
.end(); ++it
)
953 onAdded(Index(0).next(it
->first
));
954 for(VComments::const_iterator it
= e
->vcomments
.begin();
955 it
!= e
->vcomments
.end(); ++it
)
956 onSetVComment(Index(0), it
->first
, it
->second
);
959 onCurrentIndexChanged();