diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java index 80c5883..7e71764 100644 --- a/DroidFish/src/org/petero/droidfish/DroidFish.java +++ b/DroidFish/src/org/petero/droidfish/DroidFish.java @@ -74,7 +74,11 @@ import android.text.Html; import android.text.Layout; import android.text.Spannable; import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; import android.text.style.BackgroundColorSpan; +import android.text.style.ClickableSpan; import android.text.style.LeadingMarginSpan; import android.text.style.StyleSpan; import android.util.TypedValue; @@ -101,7 +105,6 @@ public class DroidFish extends Activity implements GUIInterface { // FIXME!!! book.txt (and test classes) should not be included in apk // FIXME!!! PGN view option: game continuation (for training) - // FIXME!!! Command to go to next/previous move in PGN export order. // FIXME!!! Remove invalid playerActions in PGN import (should be done in verifyChildren) // FIXME!!! Implement bookmark mechanism for positions in pgn files @@ -303,6 +306,7 @@ public class DroidFish extends Activity implements GUIInterface { status.setFocusable(false); moveListScroll.setFocusable(false); moveList.setFocusable(false); + moveList.setMovementMethod(LinkMovementMethod.getInstance()); thinking.setFocusable(false); cb = (ChessBoard)findViewById(R.id.chessboard); @@ -1889,6 +1893,22 @@ public class DroidFish extends Activity implements GUIInterface { boolean pendingNewLine = false; + /** Makes moves in the move list clickable. */ + private final static class MoveLink extends ClickableSpan { + private Node node; + MoveLink(Node n) { + node = n; + } + @Override + public void onClick(View widget) { + if (ctrl != null) + ctrl.goNode(node); + } + @Override + public void updateDrawState(TextPaint ds) { + } + } + public void processToken(Node node, int type, String token) { if ( (prevType == PgnToken.RIGHT_BRACKET) && (type != PgnToken.LEFT_BRACKET)) { @@ -1950,6 +1970,7 @@ public class DroidFish extends Activity implements GUIInterface { sb.append(token); int l1 = sb.length(); nodeToCharPos.put(node, new NodeInfo(l0, l1)); + sb.setSpan(new MoveLink(node), l0, l1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); if (endPos < l0) endPos = l0; col0 = false; if (nestLevel == 0) paraBold = true; diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java index c34b311..b105513 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java @@ -377,7 +377,7 @@ public class DroidChessController { } else { // Don't undo first white move if playing black vs computer, // because that would cause computer to immediately make - // a new move and the whole redo history will be lost. + // a new move. if (gameMode.playerWhite() || gameMode.playerBlack()) { game.redoMove(); return false; @@ -427,6 +427,26 @@ public class DroidChessController { } } + public final void goNode(Node node) { + if (node == null) + return; + ss.searchResultWanted = false; + stopAnalysis(); + stopComputerThinking(); + game.goNode(node); + ponderMove = null; + if (!humansTurn()) { + if (game.getLastMove() != null) { + game.undoMove(); + if (!humansTurn()) + game.redoMove(); + } + } + updateComputeThreads(true); + setSelection(); + updateGUI(); + } + public final int numVariations() { return game.numVariations(); } diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/Game.java b/DroidFish/src/org/petero/droidfish/gamelogic/Game.java index db28395..8cb4ced 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/Game.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/Game.java @@ -323,6 +323,12 @@ public class Game { } } + public final void goNode(Node node) { + tree.goNode(node); + pendingDrawOffer = false; + updateTimeControl(true); + } + public final void newGame() { tree = new GameTree(gameTextListener); if (computerPlayer != null) diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java b/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java index 7a0c278..48acc2a 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java @@ -571,7 +571,7 @@ public class GameTree { dos.writeUTF(tagPairs.get(i).tagValue); } Node.writeToStream(dos, rootNode); - List pathFromRoot = currentNode.getPathFromRoot(); + ArrayList pathFromRoot = currentNode.getPathFromRoot(); int pathLen = pathFromRoot.size(); dos.writeInt(pathLen); for (int i = 0; i < pathLen; i++) @@ -654,6 +654,15 @@ public class GameTree { } } + /** Go to given node in game tree. */ + public final void goNode(Node node) { + ArrayList path = node.getPathFromRoot(); + while (currentNode != rootNode) + goBack(); + for (Integer c : path) + goForward(c); + } + /** List of possible continuation moves. */ public final List variations() { if (currentNode.verifyChildren(currentPos)) @@ -985,11 +994,19 @@ public class GameTree { return anyToRemove; } - final List getPathFromRoot() { - List ret = new ArrayList(64); + final ArrayList getPathFromRoot() { + ArrayList ret = new ArrayList(64); Node node = this; while (node.parent != null) { - ret.add(node.parent.defaultChild); + Node p = node.parent; + int childNo = -1; + for (int i = 0; i < p.children.size(); i++) + if (p.children.get(i) == node) { + childNo = i; + break; + } + if (childNo == -1) throw new RuntimeException(); + ret.add(childNo); node = node.parent; } Collections.reverse(ret);