diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java b/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java index cc867c2..336352d 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java @@ -57,7 +57,6 @@ import org.petero.droidfish.gamelogic.Move; import org.petero.droidfish.gamelogic.Piece; import org.petero.droidfish.gamelogic.Position; import org.petero.droidfish.gamelogic.TextIO; -import org.petero.droidfish.gamelogic.PgnToken; import org.petero.droidfish.gamelogic.GameTree.Node; import org.petero.droidfish.gamelogic.TimeControlData; import org.petero.droidfish.tb.Probe; @@ -125,15 +124,8 @@ import androidx.core.view.MotionEventCompat; import androidx.drawerlayout.widget.DrawerLayout; import android.text.Editable; import android.text.Html; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.TextUtils; import android.text.TextWatcher; -import android.text.style.BackgroundColorSpan; -import android.text.style.ForegroundColorSpan; -import android.text.style.LeadingMarginSpan; -import android.text.style.StyleSpan; import android.util.Pair; import android.util.TypedValue; import android.view.Gravity; @@ -237,7 +229,7 @@ public class DroidFish extends Activity private boolean showVariationLine; private int autoMoveDelay; // Delay in auto forward/backward mode - private enum AutoMode { + enum AutoMode { OFF, FORWARD, BACKWARD } private AutoMode autoMode = AutoMode.OFF; @@ -3824,240 +3816,18 @@ public class DroidFish extends Activity setAutoMode(AutoMode.OFF); } - /** PngTokenReceiver implementation that renders PGN data for screen display. */ - static class PgnScreenText implements PgnToken.PgnTokenReceiver, - MoveListView.OnLinkClickListener { - private SpannableStringBuilder sb = new SpannableStringBuilder(); - private TreeMap offs2Node = new TreeMap<>(); - private int prevType = PgnToken.EOF; - int nestLevel = 0; - boolean col0 = true; - Node currNode = null; - final static int indentStep = 15; - int currPos = 0, endPos = 0; - boolean upToDate = false; - PGNOptions options; - DroidFish df; + /** Go to given node in game tree. */ + public void goNode(Node node) { + if (ctrl == null) + return; - private static class NodeInfo { - int l0, l1; - NodeInfo(int ls, int le) { - l0 = ls; - l1 = le; - } - } - HashMap nodeToCharPos; - - PgnScreenText(DroidFish df, PGNOptions options) { - this.df = df; - nodeToCharPos = new HashMap<>(); - this.options = options; - } - - public final CharSequence getText() { - return sb; - } - public final int getCurrPos() { - return currPos; - } - - @Override - public boolean isUpToDate() { - return upToDate; - } - - int paraStart = 0; - int paraIndent = 0; - boolean paraBold = false; - private void newLine() { newLine(false); } - private void newLine(boolean eof) { - if (!col0) { - if (paraIndent > 0) { - int paraEnd = sb.length(); - int indent = paraIndent * indentStep; - sb.setSpan(new LeadingMarginSpan.Standard(indent), paraStart, paraEnd, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - if (paraBold) { - int paraEnd = sb.length(); - sb.setSpan(new StyleSpan(Typeface.BOLD), paraStart, paraEnd, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - if (!eof) - sb.append('\n'); - paraStart = sb.length(); - paraIndent = nestLevel; - paraBold = false; - } - col0 = true; - } - - boolean pendingNewLine = false; - - private void addMoveLink(Node node, int l0, int l1) { - offs2Node.put(l0, node); - offs2Node.put(l1, null); - } - - @Override - public boolean onLinkClick(int offs) { - if (df.ctrl == null) - return false; - Map.Entry e = offs2Node.floorEntry(offs); - if (e == null) - return false; - Node node = e.getValue(); - if (node == null && e.getKey() == offs) { - e = offs2Node.lowerEntry(e.getKey()); - if (e != null) - node = e.getValue(); - } - if (node == null) - return false; - - // On android 4.1 this onClick method is called - // even when you long click the move list. The test - // below works around the problem. - Dialog mlmd = df.moveListMenuDlg; - if ((mlmd == null) || !mlmd.isShowing()) { - df.setAutoMode(AutoMode.OFF); - df.ctrl.goNode(node); - } - return true; - } - - @Override - public void processToken(Node node, int type, String token) { - if ( (prevType == PgnToken.RIGHT_BRACKET) && - (type != PgnToken.LEFT_BRACKET)) { - if (options.view.headers) { - col0 = false; - newLine(); - } else { - sb.clear(); - paraBold = false; - } - } - if (pendingNewLine) { - if (type != PgnToken.RIGHT_PAREN) { - newLine(); - pendingNewLine = false; - } - } - switch (type) { - case PgnToken.STRING: - sb.append(" \""); - sb.append(token); - sb.append('"'); - break; - case PgnToken.INTEGER: - if ( (prevType != PgnToken.LEFT_PAREN) && - (prevType != PgnToken.RIGHT_BRACKET) && !col0) - sb.append(' '); - sb.append(token); - col0 = false; - break; - case PgnToken.PERIOD: - sb.append('.'); - col0 = false; - break; - case PgnToken.ASTERISK: sb.append(" *"); col0 = false; break; - case PgnToken.LEFT_BRACKET: sb.append('['); col0 = false; break; - case PgnToken.RIGHT_BRACKET: sb.append("]\n"); col0 = false; break; - case PgnToken.LEFT_PAREN: - nestLevel++; - if (col0) - paraIndent++; - newLine(); - sb.append('('); - col0 = false; - break; - case PgnToken.RIGHT_PAREN: - sb.append(')'); - nestLevel--; - pendingNewLine = true; - break; - case PgnToken.NAG: - sb.append(Node.nagStr(Integer.parseInt(token))); - col0 = false; - break; - case PgnToken.SYMBOL: { - if ((prevType != PgnToken.RIGHT_BRACKET) && (prevType != PgnToken.LEFT_BRACKET) && !col0) - sb.append(' '); - int l0 = sb.length(); - sb.append(token); - int l1 = sb.length(); - nodeToCharPos.put(node, new NodeInfo(l0, l1)); - addMoveLink(node, l0, l1); - if (endPos < l0) endPos = l0; - col0 = false; - if (nestLevel == 0) paraBold = true; - break; - } - case PgnToken.COMMENT: - if (prevType == PgnToken.RIGHT_BRACKET) { - } else if (nestLevel == 0) { - nestLevel++; - newLine(); - nestLevel--; - } else { - if ((prevType != PgnToken.LEFT_PAREN) && !col0) { - sb.append(' '); - } - } - int l0 = sb.length(); - sb.append(token.replaceAll("[ \t\r\n]+", " ").trim()); - int l1 = sb.length(); - int color = ColorTheme.instance().getColor(ColorTheme.PGN_COMMENT); - sb.setSpan(new ForegroundColorSpan(color), l0, l1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - col0 = false; - if (nestLevel == 0) - newLine(); - break; - case PgnToken.EOF: - newLine(true); - upToDate = true; - break; - } - prevType = type; - } - - @Override - public void clear() { - sb = new SpannableStringBuilder(); - offs2Node.clear(); - prevType = PgnToken.EOF; - nestLevel = 0; - col0 = true; - currNode = null; - currPos = 0; - endPos = 0; - nodeToCharPos.clear(); - paraStart = 0; - paraIndent = 0; - paraBold = false; - pendingNewLine = false; - - upToDate = false; - } - - BackgroundColorSpan bgSpan = new BackgroundColorSpan(0xff888888); - - @Override - public void setCurrent(Node node) { - sb.removeSpan(bgSpan); - NodeInfo ni = nodeToCharPos.get(node); - if ((ni == null) && (node != null) && (node.getParent() != null)) - ni = nodeToCharPos.get(node.getParent()); - if (ni != null) { - int color = ColorTheme.instance().getColor(ColorTheme.CURRENT_MOVE); - bgSpan = new BackgroundColorSpan(color); - sb.setSpan(bgSpan, ni.l0, ni.l1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - currPos = ni.l0; - } else { - currPos = 0; - } - currNode = node; + // On android 4.1 this onClick method is called + // even when you long click the move list. The test + // below works around the problem. + Dialog mlmd = moveListMenuDlg; + if ((mlmd == null) || !mlmd.isShowing()) { + setAutoMode(AutoMode.OFF); + ctrl.goNode(node); } } } diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/PgnScreenText.java b/DroidFishApp/src/main/java/org/petero/droidfish/PgnScreenText.java new file mode 100644 index 0000000..3aeefb9 --- /dev/null +++ b/DroidFishApp/src/main/java/org/petero/droidfish/PgnScreenText.java @@ -0,0 +1,267 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2019 Peter Ă–sterlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish; + +import android.graphics.Typeface; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.BackgroundColorSpan; +import android.text.style.ForegroundColorSpan; +import android.text.style.LeadingMarginSpan; +import android.text.style.StyleSpan; + +import org.petero.droidfish.gamelogic.GameTree.Node; +import org.petero.droidfish.gamelogic.PgnToken; +import org.petero.droidfish.view.MoveListView; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +/** PngTokenReceiver implementation that renders PGN data for screen display. */ +class PgnScreenText implements PgnToken.PgnTokenReceiver, + MoveListView.OnLinkClickListener { + private SpannableStringBuilder sb = new SpannableStringBuilder(); + private TreeMap offs2Node = new TreeMap<>(); + private int prevType = PgnToken.EOF; + private int nestLevel = 0; + private boolean col0 = true; + private final static int indentStep = 15; + private int currPos = 0, endPos = 0; + private boolean upToDate = false; + private PGNOptions options; + private DroidFish df; + + private static class NodeInfo { + int l0, l1; + NodeInfo(int ls, int le) { + l0 = ls; + l1 = le; + } + } + private HashMap nodeToCharPos = new HashMap<>(); + + PgnScreenText(DroidFish df, PGNOptions options) { + this.df = df; + this.options = options; + } + + public final CharSequence getText() { + return sb; + } + + public final int getCurrPos() { + return currPos; + } + + @Override + public boolean isUpToDate() { + return upToDate; + } + + private int paraStart = 0; + private int paraIndent = 0; + private boolean paraBold = false; + + private void newLine() { + newLine(false); + } + + private void newLine(boolean eof) { + if (!col0) { + if (paraIndent > 0) { + int paraEnd = sb.length(); + int indent = paraIndent * indentStep; + sb.setSpan(new LeadingMarginSpan.Standard(indent), paraStart, paraEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (paraBold) { + int paraEnd = sb.length(); + sb.setSpan(new StyleSpan(Typeface.BOLD), paraStart, paraEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (!eof) + sb.append('\n'); + paraStart = sb.length(); + paraIndent = nestLevel; + paraBold = false; + } + col0 = true; + } + + private boolean pendingNewLine = false; + + private void addMoveLink(Node node, int l0, int l1) { + offs2Node.put(l0, node); + offs2Node.put(l1, null); + } + + @Override + public boolean onLinkClick(int offs) { + Map.Entry e = offs2Node.floorEntry(offs); + if (e == null) + return false; + Node node = e.getValue(); + if (node == null && e.getKey() == offs) { + e = offs2Node.lowerEntry(e.getKey()); + if (e != null) + node = e.getValue(); + } + if (node == null) + return false; + + df.goNode(node); + return true; + } + + @Override + public void processToken(Node node, int type, String token) { + if ( (prevType == PgnToken.RIGHT_BRACKET) && + (type != PgnToken.LEFT_BRACKET)) { + if (options.view.headers) { + col0 = false; + newLine(); + } else { + sb.clear(); + paraBold = false; + } + } + if (pendingNewLine) { + if (type != PgnToken.RIGHT_PAREN) { + newLine(); + pendingNewLine = false; + } + } + switch (type) { + case PgnToken.STRING: + sb.append(" \""); + sb.append(token); + sb.append('"'); + break; + case PgnToken.INTEGER: + if ( (prevType != PgnToken.LEFT_PAREN) && + (prevType != PgnToken.RIGHT_BRACKET) && !col0) + sb.append(' '); + sb.append(token); + col0 = false; + break; + case PgnToken.PERIOD: + sb.append('.'); + col0 = false; + break; + case PgnToken.ASTERISK: sb.append(" *"); col0 = false; break; + case PgnToken.LEFT_BRACKET: sb.append('['); col0 = false; break; + case PgnToken.RIGHT_BRACKET: sb.append("]\n"); col0 = false; break; + case PgnToken.LEFT_PAREN: + nestLevel++; + if (col0) + paraIndent++; + newLine(); + sb.append('('); + col0 = false; + break; + case PgnToken.RIGHT_PAREN: + sb.append(')'); + nestLevel--; + pendingNewLine = true; + break; + case PgnToken.NAG: + sb.append(Node.nagStr(Integer.parseInt(token))); + col0 = false; + break; + case PgnToken.SYMBOL: { + if ((prevType != PgnToken.RIGHT_BRACKET) && (prevType != PgnToken.LEFT_BRACKET) && !col0) + sb.append(' '); + int l0 = sb.length(); + sb.append(token); + int l1 = sb.length(); + nodeToCharPos.put(node, new NodeInfo(l0, l1)); + addMoveLink(node, l0, l1); + if (endPos < l0) + endPos = l0; + col0 = false; + if (nestLevel == 0) + paraBold = true; + break; + } + case PgnToken.COMMENT: + if (prevType != PgnToken.RIGHT_BRACKET) { + if (nestLevel == 0) { + nestLevel++; + newLine(); + nestLevel--; + } else { + if ((prevType != PgnToken.LEFT_PAREN) && !col0) + sb.append(' '); + } + } + int l0 = sb.length(); + sb.append(token.replaceAll("[ \t\r\n]+", " ").trim()); + int l1 = sb.length(); + int color = ColorTheme.instance().getColor(ColorTheme.PGN_COMMENT); + sb.setSpan(new ForegroundColorSpan(color), l0, l1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + col0 = false; + if (nestLevel == 0) + newLine(); + break; + case PgnToken.EOF: + newLine(true); + upToDate = true; + break; + } + prevType = type; + } + + @Override + public void clear() { + sb = new SpannableStringBuilder(); + offs2Node.clear(); + prevType = PgnToken.EOF; + nestLevel = 0; + col0 = true; + currPos = 0; + endPos = 0; + nodeToCharPos.clear(); + paraStart = 0; + paraIndent = 0; + paraBold = false; + pendingNewLine = false; + + upToDate = false; + } + + private BackgroundColorSpan bgSpan = new BackgroundColorSpan(0xff888888); + + @Override + public void setCurrent(Node node) { + sb.removeSpan(bgSpan); + NodeInfo ni = nodeToCharPos.get(node); + if ((ni == null) && (node != null) && (node.getParent() != null)) + ni = nodeToCharPos.get(node.getParent()); + if (ni != null) { + int color = ColorTheme.instance().getColor(ColorTheme.CURRENT_MOVE); + bgSpan = new BackgroundColorSpan(color); + sb.setSpan(bgSpan, ni.l0, ni.l1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + currPos = ni.l0; + } else { + currPos = 0; + } + } +}