// Display.java
package net.sf.gogui.tools.display;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import net.sf.gogui.go.Board;
import net.sf.gogui.go.ConstPointList;
import net.sf.gogui.go.GoColor;
import static net.sf.gogui.go.GoColor.BLACK;
import static net.sf.gogui.go.GoColor.EMPTY;
import net.sf.gogui.go.GoPoint;
import net.sf.gogui.go.Move;
import net.sf.gogui.gtp.GtpCallback;
import net.sf.gogui.gtp.GtpClient;
import net.sf.gogui.gtp.GtpCommand;
import net.sf.gogui.gtp.GtpEngine;
import net.sf.gogui.gtp.GtpError;
import net.sf.gogui.gtp.GtpResponseFormatError;
import net.sf.gogui.gtp.GtpUtil;
import net.sf.gogui.gui.AnalyzeShow;
import net.sf.gogui.gui.GuiBoard;
import net.sf.gogui.gui.GuiBoardUtil;
import net.sf.gogui.gui.GuiUtil;
import net.sf.gogui.gui.LiveGfx;
import net.sf.gogui.gui.MessageDialogs;
import net.sf.gogui.gui.StatusBar;
import net.sf.gogui.util.LineReader;
import net.sf.gogui.util.StringUtil;
/** GTP adapter showing the current board in a window. */
public class Display
extends GtpEngine
implements LiveGfx.Listener
{
public Display(String program, boolean verbose)
throws Exception
{
super(null);
m_size = GoPoint.DEFAULT_SIZE;
m_board = new Board(m_size);
m_frame = new JFrame();
m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
WindowAdapter windowAdapter = new WindowAdapter() {
public void windowClosing(WindowEvent event) {
closeFrame();
}
};
m_frame.addWindowListener(windowAdapter);
Container contentPane = m_frame.getContentPane();
m_guiBoard = new GuiBoard(m_size);
m_guiBoard.setListener(new GuiBoard.Listener() {
public void contextMenu(GoPoint point, Component invoker,
int x, int y)
{
}
public void fieldClicked(GoPoint point,
boolean modifiedSelect)
{
cbFieldClicked(point, modifiedSelect);
}
});
contentPane.add(m_guiBoard);
m_statusBar = new StatusBar();
m_statusBar.showMoveText(false);
contentPane.add(m_statusBar, BorderLayout.SOUTH);
if (! StringUtil.isEmpty(program))
{
GtpClient.IOCallback ioCallback = new GtpClient.IOCallback() {
public void receivedInvalidResponse(String s) {
}
public void receivedResponse(boolean error, String s) {
}
public void receivedStdErr(String s) {
m_lineReader.add(s);
while (m_lineReader.hasLines())
m_liveGfx.handleLine(m_lineReader.getLine());
}
public void sentCommand(String s) {
}
private final LineReader m_lineReader = new LineReader();
private LiveGfx m_liveGfx = new LiveGfx(Display.this);
};
m_gtp = new GtpClient(program, null, verbose, ioCallback);
m_gtp.queryProtocolVersion();
m_gtp.querySupportedCommands();
m_guiBoard.setShowCursor(false);
m_gtp.queryName();
m_name = m_gtp.getLabel();
String title = "gogui-display - " + m_name;
m_frame.setTitle(title);
}
else
{
m_gtp = null;
m_name = null;
m_frame.setTitle("gogui-display");
}
registerCommands();
GuiUtil.setGoIcon(m_frame);
m_frame.pack();
m_frame.setVisible(true);
}
public void close()
{
if (m_gtp != null)
{
m_gtp.close();
m_gtp.waitForExit();
}
GuiUtil.invokeAndWait(new Runnable()
{
public void run()
{
if (m_frame != null)
{
m_messageDialogs.showInfo(m_frame,
"GTP stream was closed",
"", true);
showStatus("GTP stream was closed");
}
else if (m_gtp == null)
System.exit(0);
}
});
}
public void cmdForward(GtpCommand cmd) throws GtpError
{
send(cmd.getLine(), cmd.getResponse());
}
public void cmdName(GtpCommand cmd)
{
if (m_gtp == null)
cmd.setResponse("gogui-display");
else
cmd.setResponse(m_name);
}
public void cmdQuit(GtpCommand cmd) throws GtpError
{
if (m_gtp != null)
send("quit", cmd.getResponse());
setQuit();
}
public void handleCommand(GtpCommand cmd) throws GtpError
{
showStatus(cmd.getLine());
super.handleCommand(cmd);
}
public void interruptCommand()
{
if (m_gtp == null)
return;
try
{
if (m_gtp.isInterruptSupported())
m_gtp.sendInterrupt();
}
catch (GtpError e)
{
System.err.println(e);
}
}
public void showLiveGfx(final String text)
{
assert SwingUtilities.isEventDispatchThread();
m_guiBoard.clearAll();
GuiBoardUtil.updateFromGoBoard(m_guiBoard, m_board, false, false);
AnalyzeShow.showGfx(text, m_guiBoard, m_statusBar, null);
}
/** Only accept this board size.
A value of -1 means accept any size. */
private int m_size;
private final Board m_board;
private GoColor m_color;
private final GuiBoard m_guiBoard;
private GoPoint m_fieldClicked;
private final GtpClient m_gtp;
private Move m_move;
private final Object m_mutex = new Object();
private JFrame m_frame;
private final StatusBar m_statusBar;
private final String m_name;
private final MessageDialogs m_messageDialogs = new MessageDialogs();
private void cbFieldClicked(GoPoint point, boolean modifiedSelect)
{
assert SwingUtilities.isEventDispatchThread();
if (m_board.getColor(point) != EMPTY)
return;
synchronized (m_mutex)
{
if (modifiedSelect)
m_fieldClicked = null;
else
m_fieldClicked = point;
m_mutex.notifyAll();
}
}
private void clearStatus()
{
m_statusBar.clear();
}
private void closeFrame()
{
assert SwingUtilities.isEventDispatchThread();
if (m_gtp == null)
{
if (! m_messageDialogs.showQuestion(m_frame,
"Terminate gogui-display?",
"", "Terminate", true))
return;
System.exit(0);
}
m_frame.dispose();
m_frame = null;
}
private void cmdBoardsize(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(1);
int size = cmd.getIntArg(0, 1, GoPoint.MAX_SIZE);
if (m_gtp != null)
{
String command = m_gtp.getCommandBoardsize(size);
if (command != null)
send(command);
command = m_gtp.getCommandClearBoard(size);
send(command);
}
m_size = size;
GuiUtil.invokeAndWait(new Runnable()
{
public void run()
{
m_board.init(m_size);
if (m_guiBoard.getBoardSize() != m_size)
{
m_guiBoard.initSize(m_size);
m_frame.pack();
}
updateFromGoBoard();
}
});
}
private void cmdClearBoard(GtpCommand cmd) throws GtpError
{
cmd.checkArgNone();
if (m_gtp != null)
{
String command = m_gtp.getCommandClearBoard(m_size);
send(command);
}
GuiUtil.invokeAndWait(new Runnable()
{
public void run()
{
m_board.clear();
updateFromGoBoard();
}
});
}
private void cmdGenmove(GtpCommand cmd) throws GtpError
{
GoColor color = cmd.getColorArg();
GoPoint point;
if (m_gtp == null)
{
if (m_frame == null)
// can that happen?
throw new GtpError("gogui-display terminated");
m_color = color;
showStatus("Input move for " + m_color
+ " (Ctrl-button and click for pass)");
synchronized (m_mutex)
{
try
{
m_mutex.wait();
}
catch (InterruptedException e)
{
System.err.println("InterruptedException");
}
point = m_fieldClicked;
}
cmd.setResponse(GoPoint.toString(point));
}
else
{
String command = m_gtp.getCommandGenmove(color);
StringBuilder response = cmd.getResponse();
send(command, response);
if (response.toString().trim().equalsIgnoreCase("resign"))
return;
try
{
point = GtpUtil.parsePoint(response.toString(), m_size);
}
catch (GtpResponseFormatError e)
{
throw new GtpError(e.getMessage());
}
}
m_move = Move.get(color, point);
GuiUtil.invokeAndWait(new Runnable()
{
public void run()
{
m_board.play(m_move);
updateFromGoBoard();
if (m_gtp == null)
clearStatus();
}
});
}
private void cmdKomi(GtpCommand cmd) throws GtpError
{
if (m_gtp == null)
return;
send(cmd.getLine(), cmd.getResponse());
}
private void cmdPlaceFreeHandicap(GtpCommand cmd) throws GtpError
{
int n = cmd.getIntArg();
ConstPointList stones = Board.getHandicapStones(m_size, n);
if (stones == null)
throw new GtpError("Invalid number of handicap stones");
StringBuilder pointList = new StringBuilder(128);
for (GoPoint p : stones)
{
play(BLACK, p);
if (pointList.length() > 0)
pointList.append(' ');
pointList.append(p);
}
cmd.setResponse(pointList.toString());
}
private void cmdPlay(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(2);
GoColor color = cmd.getColorArg(0);
GoPoint point = cmd.getPointArg(1, m_size);
play(color, point);
}
private void cmdSetFreeHandicap(GtpCommand cmd) throws GtpError
{
for (int i = 0; i < cmd.getNuArg(); ++i)
play(BLACK, cmd.getPointArg(i, m_size));
}
private void cmdUndo(GtpCommand cmd) throws GtpError
{
cmd.checkArgNone();
undo();
}
private void play(GoColor color, GoPoint point) throws GtpError
{
if (m_gtp != null)
{
String command = m_gtp.getCommandPlay(Move.get(color, point));
send(command);
}
m_move = Move.get(color, point);
GuiUtil.invokeAndWait(new Runnable()
{
public void run()
{
m_board.play(m_move);
updateFromGoBoard();
}
});
}
private void registerCommands()
{
if (m_gtp != null)
{
GtpCallback forwardCallback = new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdForward(cmd); } };
ArrayList<String> commands = m_gtp.getSupportedCommands();
for (String c : commands)
{
if (GtpUtil.isStateChangingCommand(c)
|| c.equals("help")
|| c.equals("known_command")
|| c.equals("komi")
|| c.equals("list_commands")
|| c.equals("protocol_version"))
continue;
register(c, forwardCallback);
}
}
register("boardsize", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdBoardsize(cmd); } });
register("clear_board", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdClearBoard(cmd); } });
register("genmove", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGenmove(cmd); } });
register("komi", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdKomi(cmd); } });
register("name", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdName(cmd); } });
register("place_free_handicap", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdPlaceFreeHandicap(cmd); } });
register("play", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdPlay(cmd); } });
register("quit", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdQuit(cmd); } });
register("set_free_handicap", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdSetFreeHandicap(cmd); } });
register("undo", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdUndo(cmd); } });
register("version", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdVersion(cmd); } });
}
private void send(String cmd, StringBuilder response) throws GtpError
{
response.append(m_gtp.send(cmd));
}
private void send(String cmd) throws GtpError
{
m_gtp.send(cmd);
}
private void showStatus(final String text)
{
Runnable runnable = new Runnable()
{
public void run()
{
if (m_frame != null)
m_statusBar.setText(text);
}
};
if (SwingUtilities.isEventDispatchThread())
runnable.run();
else
GuiUtil.invokeAndWait(runnable);
}
private void undo() throws GtpError
{
if (m_gtp != null)
send("undo");
GuiUtil.invokeAndWait(new Runnable()
{
public void run()
{
m_board.undo();
updateFromGoBoard();
}
});
}
private void updateFromGoBoard()
{
m_guiBoard.clearAll(); // Live Gfx markup
GuiBoardUtil.updateFromGoBoard(m_guiBoard, m_board, true, false);
m_statusBar.clear();
m_statusBar.setToPlay(m_board.getToMove());
}
}