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 kError() << "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 kError() << "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 kError() << "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 kError() << "Index out of range";
185 void Game::reset(PositionPtr pos
) {
189 undo_history
.clear();
191 history
.push_back( Entry(MovePtr(), pos
) );
193 onCurrentIndexChanged();
198 kError() << "Nothing to undo";
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
.back().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
);
320 kError() << "Unexpected type in boost::variant";
323 onAvailableUndo(false);
325 onAvailableRedo(true);
329 if(undo_pos
>= (int)undo_history
.size()) {
330 kError() << "Nothing to redo";
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
.back().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 onCurrentIndexChanged();
367 else if(boost::get
<UndoPromote
>(op
)) {
368 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
371 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
374 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
375 History vold
= (*vec
)[at
].variations
[p
->variation
];
377 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
378 vnew
.push_back((*vec
)[i
]);
379 while((int)vec
->size()>at
+1)
381 for(int i
=0; i
<(int)vold
.size(); i
++)
382 vec
->push_back(vold
[i
]);
383 (*vec
)[at
].variations
[p
->variation
] = vnew
;
385 current
= current
.flipVariation(p
->index
, p
->variation
);
386 onPromoteVariation(p
->index
, p
->variation
);
387 //onCurrentIndexChanged();
389 else if(boost::get
<UndoTruncate
>(op
)) {
390 UndoTruncate
*t
= boost::get
<UndoTruncate
>(op
);
393 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
395 Q_ASSERT((int)vec
->size() == at
+1+(int)t
->history
.size());
397 while((int)vec
->size() > at
+1)
399 (*vec
)[at
].variations
.clear();
400 (*vec
)[at
].vcomments
.clear();
402 if(current
> t
->index
) {
404 onCurrentIndexChanged();
407 if(t
->history
.size())
408 onRemoved(t
->index
.next());
409 for(Variations::iterator it
= t
->variations
.begin(); it
!= t
->variations
.end(); ++it
)
410 onRemoved(t
->index
.next(it
->first
));
412 else if(boost::get
<UndoRemove
>(op
)) {
413 UndoRemove
*r
= boost::get
<UndoRemove
>(op
);
415 Entry
*e
= fetch(r
->index
);
416 e
->variations
.erase(r
->variation
);
417 e
->vcomments
.erase(r
->variation
);
418 onRemoved(r
->index
.next(r
->variation
));
420 else if(boost::get
<UndoClear
>(op
)) {
421 UndoClear
*c
= boost::get
<UndoClear
>(op
);
423 Entry
*e
= fetch(c
->index
);
424 e
->variations
.clear();
425 e
->vcomments
.clear();
426 for(Variations::iterator it
= c
->variations
.begin(); it
!= c
->variations
.end(); ++it
)
427 onRemoved(c
->index
.next(it
->first
));
429 else if(boost::get
<UndoSetComment
>(op
)) {
430 UndoSetComment
*sc
= boost::get
<UndoSetComment
>(op
);
431 Entry
*e
= fetch(sc
->index
);
434 if(sc
->variation
== -1) {
435 e
->comment
= sc
->new_comment
;
436 onSetComment(sc
->index
, sc
->new_comment
);
439 if(sc
->new_comment
.isEmpty())
440 e
->vcomments
.erase(sc
->variation
);
442 e
->vcomments
[sc
->variation
] = sc
->new_comment
;
443 onSetVComment(sc
->index
, sc
->variation
, sc
->new_comment
);
447 kError() << "Unexpected type in boost::variant";
450 onAvailableUndo(true);
452 onAvailableRedo(false);
455 void Game::setComment(const QString
& c
) {
456 setComment(current
, c
);
459 void Game::setComment(const Index
& ix
, const QString
& c
) {
460 Entry
* e
= fetch(ix
);
462 kError() << "Invalid index";
468 saveUndo(UndoSetComment(ix
, -1, e
->comment
, c
));
473 void Game::setVComment(const Index
& ix
, int v
, const QString
& c
) {
474 Entry
* e
= fetch(ix
);
476 kError() << "Invalid index";
479 QString oc
= e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString();
483 saveUndo(UndoSetComment(ix
, v
, oc
, c
));
485 e
->vcomments
.erase(v
);
488 onSetVComment(ix
, v
, c
);
491 void Game::promoteVariation() {
492 promoteVariation(current
);
495 void Game::promoteVariation(const Index
& _ix
) {
496 if(_ix
.nested
.size()==0) {
497 kError() << "Cannot promote main line";
501 int v
= ix
.nested
.back().variation
;
502 ix
.nested
.pop_back();
504 promoteVariation(ix
, v
);
507 void Game::promoteVariation(const Index
& ix
, int v
) {
509 std::vector
<Entry
>* vec
= fetchRef(ix
, &at
);
511 Q_ASSERT((*vec
)[at
].variations
.count(v
)==1);
513 History vold
= (*vec
)[at
].variations
[v
];
515 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
516 vnew
.push_back((*vec
)[i
]);
517 while((int)vec
->size()>at
+1)
519 for(int i
=0; i
<(int)vold
.size(); i
++)
520 vec
->push_back(vold
[i
]);
521 (*vec
)[at
].variations
[v
] = vnew
;
523 saveUndo(UndoPromote(ix
, v
));
524 current
= current
.flipVariation(ix
, v
);
525 onPromoteVariation(ix
, v
);
526 //don't call onCurrentIndexChanged(), as the position did not change actually
529 void Game::removeVariation(int v
) {
530 removeVariation(current
, v
);
533 void Game::removeVariation(const Index
& _ix
) {
534 if(_ix
.nested
.size()==0) {
535 kError() << "Cannot remove main line";
539 int v
= ix
.nested
.back().variation
;
540 ix
.nested
.pop_back();
542 removeVariation(ix
, v
);
545 void Game::removeVariation(const Index
& ix
, int v
) {
546 Entry
* e
= fetch(ix
);
548 saveUndo(UndoRemove(ix
, v
, e
->variations
[v
],
549 e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString() ));
550 e
->variations
.erase(v
);
551 e
->vcomments
.erase(v
);
553 onRemoved(ix
.next(v
));
554 if(current
>= ix
.next(v
)) {
556 onCurrentIndexChanged();
560 void Game::clearVariations() {
561 clearVariations(current
);
564 void Game::clearVariations(const Index
& ix
) {
565 Entry
* e
= fetch(ix
);
567 UndoClear
uc(ix
, e
->variations
, e
->vcomments
);
569 e
->variations
.clear();
570 e
->vcomments
.clear();
572 for(Variations::iterator it
= uc
.variations
.begin(); it
!= uc
.variations
.end(); ++it
)
573 onRemoved(ix
.next(it
->first
));
574 if(current
> ix
&& !(current
>= ix
.next())) {
576 onCurrentIndexChanged();
580 void Game::truncate() {
584 void Game::truncate(const Index
& ix
) {
586 History
* vec
= fetchRef(ix
, &at
);
588 kError() << "Truncating at an unexisting index";
592 Entry
*e
= &(*vec
)[at
];
593 UndoTruncate
undo(ix
);
594 for(int i
=at
+1; i
<(int)vec
->size();i
++)
595 undo
.history
.push_back((*vec
)[i
]);
596 while((int)vec
->size()>at
+1)
599 undo
.variations
= e
->variations
;
600 undo
.vcomments
= e
->vcomments
;
602 e
->variations
.clear();
603 e
->vcomments
.clear();
605 if(undo
.history
.size())
606 onRemoved(undo
.index
.next());
607 for(Variations::iterator it
= undo
.variations
.begin(); it
!= undo
.variations
.end(); ++it
)
608 onRemoved(undo
.index
.next(it
->first
));
612 onCurrentIndexChanged();
616 void Game::add(MovePtr m
, PositionPtr pos
) {
619 Index old_c
= current
;
621 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
624 /* add the move on the mainline */
625 if((int)vec
->size() <= at
+1 ) {
626 Q_ASSERT((int)vec
->size() == at
+1);
627 vec
->push_back(Entry(m
, pos
));
628 current
= current
.next();
630 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
632 onCurrentIndexChanged(old_c
);
634 /* we are playing the move that is already next in the mainline */
635 else if( (*vec
)[at
+1].position
&& (*vec
)[at
+1].position
->equals(pos
)) {
636 current
= current
.next();
637 onCurrentIndexChanged(old_c
);
638 /* no need to test the move */
641 Entry
*e
= fetch(current
);
644 /* check if a variations with this move already exists. */
645 for(Variations::iterator it
= e
->variations
.begin(); it
!= e
->variations
.end(); ++it
)
646 if(it
->second
.size() > 0 && it
->second
[0].position
647 && it
->second
[0].position
->equals(pos
) ) {
648 current
= current
.next(it
->first
);
649 onCurrentIndexChanged(old_c
);
654 int var_id
= e
->last_var_id
++;
655 e
->variations
[var_id
].push_back(Entry(m
, pos
));
656 current
= current
.next(var_id
);
658 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
660 onCurrentIndexChanged(old_c
);
664 bool Game::insert(MovePtr m
, PositionPtr pos
, const Index
& at
) {
665 Entry
*e
= fetch(at
);
668 if(at
.nested
.size() == 0) {
669 if(undo_history
.size()) {
671 undo_history
.clear();
673 int hs
= history
.size();
674 history
.resize(at
.num_moves
+ 1);
675 history
[at
.num_moves
] = Entry(m
, pos
);
681 kError() << "Index out if range";
686 if(undo_history
.size()) {
688 undo_history
.clear();
690 bool res
= e
->position
&& e
->position
->equals(pos
);
695 for (Variations::const_iterator it
= e
->variations
.begin();
696 it
!= e
->variations
.end(); ++it
)
697 testMove(at
.next(it
->first
));
702 bool Game::lastPosition() const {
703 return !fetch(current
.next());
707 if (current
<= 0) return false; // first entry or uninitialized
708 Index old_c
= current
;
709 Index new_c
= current
.prev();
711 Entry
*e
= fetch(new_c
);
712 if(!e
|| e
->position
== 0) return false; // gap immediately before current
714 onCurrentIndexChanged(old_c
);
719 bool Game::forward() {
720 Index old_c
= current
;
721 Index new_c
= current
.next();
723 Entry
*e
= fetch(new_c
);
724 if(!e
|| e
->position
== 0) {
725 return false; // gap immediately before current
728 onCurrentIndexChanged(old_c
);
733 void Game::gotoFirst() {
734 Index old_c
= current
;
736 onCurrentIndexChanged(old_c
);
739 void Game::gotoLast() {
741 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
743 Q_ASSERT((int)vec
->size() > at
);
745 if((int)vec
->size() > at
+1) {
746 Index old_c
= current
;
747 current
= current
.next(-1, vec
->size()-1-at
);
748 onCurrentIndexChanged(old_c
);
752 bool Game::goTo(const Index
& index
) {
754 Index old_c
= current
;
756 onCurrentIndexChanged(old_c
);
762 QString
Game::variationPgn(const History
& vec
, const Entry
& e
,
763 int start
, const Index
& _ix
) const {
767 for (int i
= start
; i
< static_cast<int>(vec
.size()); i
++) {
768 const Entry
& preve
= (i
> start
) ? vec
[i
-1] : e
;
770 QString mv
= (vec
[i
].move
&& preve
.position
) ?
771 vec
[i
].move
->toString("compact", preve
.position
) : "???";
774 mv
= "[[" + mv
+ "]]";
777 int n
= ix
.totalNumMoves()+1;
778 if(i
==start
|| n
%2==0)
779 mv
= QString::number(n
/2)+(n
%2==1 ? ". ... " : ". ") + mv
;
785 if(!vec
[i
].comment
.isEmpty())
786 res
+= " {" + vec
[i
].comment
+ "}";
789 for(Variations::const_iterator it
= vec
[i
-1].variations
.begin();
790 it
!= vec
[i
-1].variations
.end(); ++it
) {
792 if(vec
[i
-1].vcomments
.count(it
->first
))
793 res
+= "{" + vec
[i
-1].vcomments
.find(it
->first
)->second
+ "} ";
794 res
+= variationPgn(it
->second
, vec
[i
- 1], 0,
795 ix
.prev().next(it
->first
)) + ")";
804 QString
Game::pgn() const {
805 return variationPgn(history
, history
[0], 1, Index(1));
808 void Game::load(const PGN
& pgn
) {
809 std::map
<QString
, QString
>::const_iterator var
= pgn
.m_tags
.find("Variant");
812 if (var
== pgn
.m_tags
.end()) {
813 vi
= Variants::instance().get("chess");
815 else if (!(vi
= Variants::instance().get(var
->second
))) {
816 kError() << "No such variant" << var
->second
;
820 std::map
<QString
, QString
>::const_iterator fen
= pgn
.m_tags
.find("FEN");
823 //if(var == pgn.m_tags.end()) {
824 pos
= vi
->createPosition();
828 else if( !(pos
= vi
->createPositionFromFEN(fen
->second
))) {
829 kError() << "Wrong fen " << fen
->second
;
834 //TODO: what about options? FEN rules?
839 void Game::load(PositionPtr pos
, const PGN
& pgn
) {
841 undo_history
.clear();
844 // setup an empty history, clear as needed
847 Entry
* fe
= &history
[0];
848 int old_history_size
= history
.size();
849 std::vector
<int> v_ids
;
851 while(history
.size()>1)
853 for(Variations::const_iterator it
= fe
->variations
.begin();
854 it
!= fe
->variations
.end(); ++it
)
855 v_ids
.push_back(it
->first
);
856 fe
->variations
.clear();
857 fe
->vcomments
.clear();
859 for(int i
=0;i
<(int)v_ids
.size();i
++)
860 onRemoved(Index(0).next(v_ids
[i
]));
861 if(old_history_size
>1)
864 history
[0].position
= pos
;
867 history
.push_back( Entry(MovePtr(), pos
) );
869 // apply moves from PGN, one by one
872 std::vector
<Index
> var_stack
;
873 bool var_start
= false;
875 for (uint i
= 0; i
< pgn
.m_entries
.size(); i
++) {
876 if(boost::get
<QString
>(pgn
[i
])) {
878 vcomment
+= *boost::get
<QString
>(pgn
[i
]);
880 Entry
*e
= fetch(current
);
883 e
->comment
+= *boost::get
<QString
>(pgn
[i
]);
886 else if(boost::get
<PGN::BeginVariation
>(pgn
[i
])) {
887 var_stack
.push_back(current
);
890 else if(boost::get
<PGN::EndVariation
>(pgn
[i
])) {
891 if(var_stack
.size() == 0) {
892 kError() << "Unexpected end variation";
895 current
= var_stack
.back();
896 var_stack
.pop_back();
898 else if(boost::get
<PGN::Move
>(pgn
[i
])) {
899 const PGN::Move
*pm
= boost::get
<PGN::Move
>(pgn
[i
]);
901 int n
= current
.totalNumMoves()+1;
903 if(!pm
->m_number
) // not all moves get numbered in PGN, usually only 1st player ones
904 current
= current
.prev();
905 else if(pm
->m_number
>n
+1)
906 kError() << "Too far variation";
909 kError() << "Too close variation";
910 current
= current
.prev(n
+ 1 - pm
->m_number
);
913 else if(pm
->m_number
&& pm
->m_number
!=n
+1)
914 kError() << "Move number mismatch";
916 PositionPtr pos
= position();
917 MovePtr m
= pos
->getMove(pm
->m_move
);
919 if(!m
|| !pos
->testMove(m
))
922 PositionPtr newPos
= pos
->clone();
926 History
*vec
= fetchRef(current
, &at
);
930 Entry
*e
= &(*vec
)[at
];
931 int var_id
= e
->last_var_id
++;
932 e
->variations
[var_id
].push_back(Entry(m
, newPos
));
933 if(!vcomment
.isEmpty()) {
934 e
->vcomments
[var_id
] = vcomment
;
935 vcomment
= QString();
937 /* this is a hack, but the mainline should NEVER
938 be empty if there is a variation*/
939 if((int)vec
->size() - 1 == at
)
940 vec
->push_back(Entry(m
, newPos
));
942 current
= current
.next(var_id
);
945 if((int)vec
->size() - 1 == at
)
946 vec
->push_back(Entry(m
, newPos
));
948 (*vec
)[at
] = Entry(m
, newPos
);
950 current
= current
.next();
956 kError() << "Unexpected type in boost::variant";
961 Entry
* e
= fetch(Index(0));
962 for(Variations::const_iterator it
= e
->variations
.begin();
963 it
!= e
->variations
.end(); ++it
)
964 onAdded(Index(0).next(it
->first
));
965 for(VComments::const_iterator it
= e
->vcomments
.begin();
966 it
!= e
->vcomments
.end(); ++it
)
967 onSetVComment(Index(0), it
->first
, it
->second
);
970 onCurrentIndexChanged();