Move PgnScreenText class to a new file

This commit is contained in:
Peter Osterlund 2019-09-21 20:51:05 +02:00
parent 5a0493a4e6
commit da38bed581
2 changed files with 279 additions and 242 deletions

View File

@ -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<Integer,Node> 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<Node, NodeInfo> 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<Integer, Node> 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);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Integer, Node> 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<Node, NodeInfo> 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<Integer, Node> 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;
}
}
}