📄 boardcanvas.java
字号:
*/
private boolean regenerateRequired = false;
public void update (Graphics g) {
if (getSize().width != width || getSize().height != height) {
WoodGenerator.release(sideSize); // remove the generator's bitmap at the old size.
width = getSize().width;
height = getSize().height;
sideSize = Math.min(width, height); // note that sideSize includes bevel.
xoffset = (width - sideSize) / 2 + getBevelWidth();
yoffset = (height - sideSize) / 2 + getBevelWidth();
cellSize = (sideSize - 2 * getBevelWidth()) / gameSize;
oodboard = WoodGenerator.request(sideSize, getBevelWidth(), this);
if (offscreen != null)
offscreen.flush();
offscreen = createImage(sideSize, sideSize);
// in this case, and in this case only, must we re-draw ALL the
// elements of offscreen.....
tempblit.reinit(cellSize, cellSize);
// +++ Should tempblitIm be flush()ed here???
tempblitIm = createImage(tempblit.mis());
if (offg != null)
offg.dispose();
offg = offscreen.getGraphics();
if (staticg != null)
staticg.dispose();
staticg = getGraphics();
regenerateRequired = true;
}
if (regenerateRequired) {
// Note that it's important to set regenerateRequired to false immediately
// here, to avoid a timing problem that will cause the wood board not to
// be displayed sometimes. It took a while to track down this bug...
regenerateRequired = false;
stoneManufac();
drawAll(offg);
decorateStone(offg);
}
g.drawImage(offscreen, 0, 0, null);
}
// This is called directly by the AWT when the component is exposed (or resized?).
//
public void paint (Graphics g) {
update(g);
}
private Position previousShadow = new Position(-1, -1);
private static StoneMove tempMove;
private static ILMV tempILMV = new ILMV();
private static Position tempPosition = new Position(0, 0);
// +++ In the case of server games this needs to display a temporary
// stone over the position the user clicks on, which will be replaced
// by a permanent stone once the move has been received from the
// server.
// !!!!! ACK. Incredible dependence on gwin. Logic should be moved,
// as per comment below.
// One idea:
// class GameStateInfo {
// Move currentMove();
// boolean canPlay();
// boolean ourTurn();
// boolean scoringMode();
// void placeMove(int row, int column); //?
// }
// class GameResult {
// boolean isScoring();
// }
private boolean mouseCommon (int x, int y, boolean exited, boolean moved,
boolean released) {
// First we must determine among these situations:
// 1. A local game - both players play, all the time
// 2. A server game -
// a) observing - play if browsing
// b) a match - play if browsing, or it is our move, or scoring
// c) a teaching game - play both all the time.
// this logic will be moved to Game, GameWindow, or Move, someday..
if (cellSize == 0) return true; // in some bizarre case, we receive a
// mouse event BEFORE constructor finishes
int thisColor = Move.nextColor(currentMove.color());
boolean ourTurn = (gwin.game.scoringMode()
|| gwin.ourColor() == thisColor
|| gwin.isTeachingGame());
int col = xtocol(x);
int row = ytorow(y);
boolean onBoard = true;
if (col < 0 || row < 0 || col >= gameSize || row >= gameSize
|| exited)
onBoard = false;
boolean isEmpty = (onBoard && ss.readStone(row, col) == Move.EMPTY);
boolean canPlay
= onBoard &&
(gwin.isLocalGame()
|| gwin.isBrowsing()
|| gwin.isParticipating()
&& ourTurn
&& onBoard
// Can't place a move on an empty position during actual
// server game scoring. (i.e., not in browsing mode.)
&& !(gwin.game.scoringMode() && !gwin.isBrowsing() && isEmpty)
&& !(currentMove instanceof GameResultMove)
);
/*
Debug.println("gwin.isParticipating() = " + gwin.isParticipating());
Debug.println("ourTurn = " + ourTurn);
Debug.println("gwin.game.scoringMode() = " + gwin.game.scoringMode());
Debug.println("stone = " + (gwin.game.ss.readStone(row, col) != Move.EMPTY));
Debug.println("canPlay = " + canPlay);
*/
boolean shadowPossible = onBoard && canPlay;
if (moved || exited) {
// now think about shadow stones:
if (shadowPossible && previousShadow.row == row
&& previousShadow.column == col)
return true; // no action if shadow has not moved.
if (previousShadow.row != -1) {
// there was a previous shadow, and it's not here:
reflectis(previousShadow); // remove it
}
boolean isLegal = false;
if (shadowPossible) {
// there is a current shadow, test heinously quickly for legality.
tempPosition.setPosition(row, col);
if (tempMove == null)
tempMove = new StoneMove(currentMove, 0, currentMove.moveNumber() + 1,
thisColor, false, tempPosition);
else
tempMove.reseatforShadow(tempPosition, thisColor, currentMove,
currentMove.moveNumber() + 1);
isLegal = tempMove.isLegal(gwin.game, true, tempILMV);
// && !(gwin.game.scoringMode() && !gwin.isBrowsing()
// && (ss.readStone(tempPosition.row,
// tempPosition.column)
// == Move.EMPTY));
}
if (isLegal) { // there is a current legal shadow, draw it
if (thisColor == Move.BLACK)
drawStone(offg, row, col, Move.BLACKSHADOW);
else if (thisColor == Move.WHITE)
drawStone(offg, row, col, Move.WHITESHADOW);
invalidateInternal(row, col);
updateInternal();
previousShadow.row = row;
previousShadow.column = col;
}
else previousShadow.row = -1; // if there is not, store the fact.
}
else if (released && canPlay) {
// Commented out the following line until I get the sound done
// right. For now the user will just have to toggle bell on.
//gwin.window.ergo.thwak();
gwin.placeMove(row, col);
ss.emit();
}
return true;
}
class mouseMotionListener extends MouseMotionAdapter {
public void mouseMoved (MouseEvent e) {
mouseCommon(e.getX(), e.getY(), false, true, false);
}
}
class mouseListener extends MouseAdapter {
// +++ Remove this when/if Java bug #4143473 is fixed. The problem is that
// MouseEvent.isPopupTrigger() returns different values during mousePressed()
// and mouseReleased(). In addition, in Unix isPopupTrigger() returns true
// in mousePressed() while in Windows+OS/2 it returns true in mouseReleased().
// As far as I can tell this makes it impossible to write portable code that
// tries to do one action when it's not the popup trigger and a different action
// when it is, as I'm trying to do here.
//
// The bug is not fixed as of 1.1.7. -cgay 99-01-16
//
public void mouseReleased (MouseEvent e) {
if (Util.isUnix()) {
if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == 0)
mouseCommon(e.getX(), e.getY(), false, false, true);
}
else if (!e.isPopupTrigger())
mouseCommon(e.getX(), e.getY(), false, false, true);
}
public void mouseExited (MouseEvent e) {
mouseCommon(e.getX(), e.getY(), true, false, false);
}
}
private void setWoodBoard (RawImage newboard) {
synchronized(ss) {
oodboard = newboard;
}
}
private int currentBoardStyle () {
synchronized(ss) {
return ((oodboard == null && boardStyle == BOARD_WOOD)
? BOARD_BASIC
: boardStyle);
}
}
public void refreshLocal () {
synchronized(ss) {
// It may have arrived now.
oodboard = WoodGenerator.request(sideSize, getBevelWidth(), this);
// ---*** The following debug line causes a deadlock. I should investigate.
//Debug.tprintln("refreshLocal(): oodboard = " + oodboard);
regenerateRequired = true;
}
repaint();
}
} // end class BoardCanvas
/**
* This class manages off-thread generation and storage of wood bitmap,
* which is currently pretty unoptimised. Even when it is optimised,
* it'll never be an acceptable delay on slow and sub-Pentium systems,
* so must be asynchronous. AMB.
*/
class WoodGenerator extends Thread {
/** Used to represent one request for a wood board.
*/
private static class WorkEntry {
int sideSize;
int bevelWidth;
// I think toRefresh should be a Vector of Refreshables, so if several boards
// request the same size they will both be refreshed when it's done. This
// may be fairly common case since boards are created with the same size as
// the most recently created board. Need to check if this is an issue... -sigue
Refreshable toRefresh;
WorkEntry (int sideSize, int bevelWidth, Refreshable toRefresh) {
this.sideSize = sideSize;
this.toRefresh = toRefresh;
this.bevelWidth = bevelWidth;
}
}
private static final Vector done = new Vector();
private static final Vector todo = new Vector();
private static final WoodGenerator woodWorker = new WoodGenerator();
static {
woodWorker.setName("Wood"); // For debugging, but harmless.
woodWorker.start();
}
private static RawImage rawImageAt (int i) {
return (RawImage)done.elementAt(i);
}
private static WorkEntry workAt (int i) {
return (WorkEntry)todo.elementAt(i);
}
// Public interface of class:
// ---*** It seems to me the argument should be a Refreshable, and anything
// with that refreshable should be released, else one board might
// release an entry when another board still needs it. -sigue
public static void release (int sideSize) {
for (int i = 0; i < done.size(); ++i) {
RawImage look = rawImageAt(i);
if (look.width == sideSize) {
done.removeElementAt(i);
}
}
synchronized (woodWorker) {
for (int i = todo.size() - 1; i >= 0; --i) {
WorkEntry work = workAt(i);
if (work.sideSize == sideSize) {
todo.removeElementAt(i);
}
}
}
}
public static RawImage request (int sideSize, int bevelWidth, Refreshable r) {
return woodWorker.requesti(sideSize, bevelWidth, r);
}
// Must be instance method, or, peculiarly, cannot have monitor.
//
private RawImage requesti (int sideSize, int bevelWidth, Refreshable r) {
if (sideSize < 1)
return null; // Don't give me that trash, you grotty heap of parrot droppings!
// You wot?
// You 'eard me, you snivelling little git!
// ...
for (int i = 0; i < done.size(); ++i) {
RawImage look = rawImageAt(i);
if (look.width == sideSize) {
return look;
}
}
for (int i = 0; i < todo.size(); ++i) {
if (workAt(i).sideSize == sideSize) {
return null;
}
}
synchronized (woodWorker) {
todo.addElement(new WorkEntry(sideSize, bevelWidth, r));
notify();
}
return null;
}
public void run () {
while (true) {
if (todo.size() == 0) {
synchronized (woodWorker) {
try {
wait();
} catch (InterruptedException ie) {}
} // end sync
} // end if nothing to do
Debug.asser(todo.size() > 0, "Error: woken up thread, but nothing to do");
WorkEntry active = workAt(0);
RawImage wood = OodInnaBox.Ood(active.sideSize, active.bevelWidth);
synchronized (woodWorker) {
if (workAt(0).sideSize != active.sideSize) {
Debug.println("REQUEST CANCELLED!!");
}
else {
done.addElement(wood);
todo.removeElementAt(0);
active.toRefresh.refreshLocal();
}
} // end sync block
} // end loop forever
}
} // end class WoodGenerator
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -