📄 gamewindow.java
字号:
package ergo.ui;
/// $Id: GameWindow.java,v 1.9 1999/08/29 02:42:24 sigue Exp $
/*
* Copyright (C) 1999 Carl L. Gay and Antranig M. Basman.
* See the file copyright.txt, distributed with this software,
* for further information.
*/
import ergo.Ergo;
import ergo.GlobalOptions;
import ergo.util.*;
import ergo.logic.*;
import ergo.server.ServerConnection;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.util.Vector;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import nom.rb.common.*;
/**
* A GameWindow is responsible for coordinating a single game (playing
* or observing). It handles all user input for that game, i.e., menus
* and mousing. The two important objects it deals with are the Game
* object which handles the game logic, and the BoardCanvas, which does
* all drawing on the board.
* <p>
* Structural note - the thread for this Frame is created in
* TerminalWindow.addGameWindow().... AMB at 0.7
*/
public class GameWindow extends Frame
implements Runnable, WindowsMenuCommand, Optionizable, OutputFocus, PopupContributor {
public static final int PLAYING = 0, ADJOURNED = 1, GAMEOVER = 2, SCORING = 3;
private PopupController popupControl;
private PopupHandler popupHandler;
// Default background is "cream" colored.
// The string is parsed as a ColorSpec.
public static final String gwinDefaultBackground = "255 255 230";
// Question: Is the SCORING state in use? AMB 0.8
// isOnServer() below assumes that PLAYING is enough.
// I think it is used. -sigue
public ServerConnection conn = null;
int gameNumber; // 0 if game invalid, -1 if local game
int gameStatus; // PLAYING, ADJOURNED, GAMEOVER, etc.
private boolean isParticipating; // Whether the user is a player in this game.
private int ourColor;
public Game game; // The game record. Should be private.
TerminalWindow window;
Optionizer opser = Ergo.opser;
private boolean browsing = false; // Set with setBrowsing().
private boolean savedBrowsing; // used by scoring mode.
private int jumpSize = 20; // Number of moves to jump forward or back.
private String filename = null; // Filename associated with this game.
private boolean needsSaving = false;
private CommandHistory commandHistory = new CommandHistory();
boolean showKibitzes = true;
private boolean showKibitzesForThisMoveOnly = false;
private boolean tallConfiguration = true;
// Components of the game window
private Panel topPanel = new GWPanel(); // top, containing goban and status
private Panel bottomPanel = new GWPanel(); // bottom, containing kibitz, etc
private CoordinateCanvas northCanvas;
private CoordinateCanvas southCanvas;
private CoordinateCanvas eastCanvas;
private CoordinateCanvas westCanvas;
private StatusPanel statusPanel;
private BoardCanvas boardCanvas;
private Panel boardPanel = new GWPanel();
private TextArea kibitzArea = new TextArea(6, 40, null, true);
private TextField inputField = new TextField();
private mouseListener mouseListen = new mouseListener();
private ImageButton makeImageButton (String filename) {
Image button_image = Ergo.loadImage("/" + Ergo.mmdir + filename);
ImageButton ib = new ImageButton(button_image);
ib.addMouseListener(mouseListen);
return ib;
}
/***
*
* The browseButton was partially implemented and then removed. Search
* for browseButton.
private Button browseButton = null; // Label changes: "Browse" <-> "Stop Browsing"
***/
private ImageButton stepBackButton;
private ImageButton stepForwardButton;
//private ImageButton jumpBackButton;
//private ImageButton jumpForwardButton;
private ImageButton toBeginningButton;
private ImageButton toEndButton;
private ImageButton fwdKibButton;
private ImageButton backKibButton;
private ImageButton fwdBranchButton;
private ImageButton backBranchButton;
PositionVector starPoints;
// Menus, menu items, and the menubar.
// The reason that these are all named variables is so that they can be
// compared with == instead of using string-equal. This way, if they are
// to be changed it is only necessary to update the string in one place
// in the code.
private urMenuBar menuBar = new urMenuBar(this);
private menuListener mListen = new menuListener();
private checkboxListener cListen = new checkboxListener();
private exclusiveCheckboxListener xListen = new exclusiveCheckboxListener();
// File menu
private Menu fileMenu = menuBar.generate("File");
private MenuItem saveItem = mListen.generate(fileMenu, "Save", new MenuShortcut('s'));
private MenuItem saveAsItem
= mListen.generate(fileMenu, "Save As...", new MenuShortcut('s', true));
private MenuItem saveOutputItem = mListen.generate(fileMenu, "Save Terminal Output...");
private MenuItem loadItem
= mListen.generate(fileMenu, "Open Game...", new MenuShortcut('o', true));
private MenuItem closeItem
= mListen.generateWithSep(fileMenu, "Close", true, new MenuShortcut('w'));
// View menu
private Menu viewMenu = menuBar.generate("View");
private Menu refreshMenu = mListen.generateMenuWithSep(viewMenu, "Refresh");
// Refresh submenu
private MenuItem refreshLocalItem = mListen.generate(refreshMenu, "Local",
new MenuShortcut('r'));
private MenuItem refreshHostItem = mListen.generate(refreshMenu, "Server",
new MenuShortcut('r', true));
private CheckboxMenuItem focusHereItem
= cListen.generate(viewMenu, "Show Terminal Output");
private CheckboxMenuItem showCoordinatesItem
= cListen.generate(viewMenu, "Show Coordinates");
boolean showCoordinates = true;
private CheckboxMenuItem showVariationsItem
= cListen.generate(viewMenu, "Show Variations");
boolean showVariations;
// Show Status submenu
private Menu showStatusMenu = mListen.generateMenu(viewMenu, "Show Status");
private CBMIGroup statusGroup = new CBMIGroup(xListen, showStatusMenu);
private CheckboxMenuItem showStatusNoneItem = statusGroup.generate("None");
private CheckboxMenuItem showStatusRightItem = statusGroup.generate("Right");
private CheckboxMenuItem showStatusBottomItem = statusGroup.generate("Bottom");
boolean showStatus;
// Kibitz submenu
private Menu kibitzMenu = mListen.generateMenu(viewMenu, "Show Kibitzes");
private CBMIGroup kibitzGroup = new CBMIGroup(xListen, kibitzMenu);
private CheckboxMenuItem kibitzAllItem = kibitzGroup.generate("All");
private CheckboxMenuItem kibitzCurrentItem = kibitzGroup.generate("Current move");
private CheckboxMenuItem kibitzNoneItem = kibitzGroup.generate("None");
// Stones submenu
private Menu stonesMenu = mListen.generateMenu(viewMenu, "Stones style");
private CBMIGroup stonesGroup = new CBMIGroup(xListen, stonesMenu);
// MUST AGREE with Styles Interface:
private CheckboxMenuItem stonesItems[] = new CheckboxMenuItem[] {
stonesGroup.generate("Basic"),
stonesGroup.generate("Anti-aliased"),
stonesGroup.generate("3d")
};
// Board submenu
private Menu boardMenu = mListen.generateMenu(viewMenu, "Board style");
private CBMIGroup boardGroup = new CBMIGroup(xListen, boardMenu);
// MUST AGREE with Styles Interface:
private CheckboxMenuItem boardItems[] = new CheckboxMenuItem[] {
boardGroup.generate("Basic"), boardGroup.generate("Wooden")
};
// Options menu
private Menu optionsMenu = menuBar.generate("Options");
private CheckboxMenuItem browsingItem
= cListen.generate(optionsMenu, "Browse");
private CheckboxMenuItem soundItem = cListen.generate(optionsMenu, "Sound");
private boolean sound = opser.getBooleanOption(GlobalOptions.soundString);
private boolean focusHere;
// This should really go in an Edit menu, but this'll do for now.
private String copyAsSGFString = "Copy (as SGF)";
private MenuItem copyAsSGFItem
= mListen.generateWithSep(optionsMenu, copyAsSGFString, true, new MenuShortcut('c'));
// Windows menu
// (Note that items added to the windows menu are
// handled specially because the items are duplicated for each window.)
private Menu windowsMenu = menuBar.generate("Window");
private static WindowsMenuCommand duplicateItem
= new WindowsMenuCommand () {
public String menuString () { return "Duplicate Window"; }
public MenuShortcut menuShortcut () {
return new MenuShortcut('d');
}
public int menuPriority () { return 10; }
public void menuSelect (Frame f) {
if (f instanceof GameWindow)
((GameWindow) f).duplicate();
}
public Class appliesTo () {
try {
return Class.forName("ergo.ui.GameWindow");
} catch (ClassNotFoundException myDogHasFleas) { return null; }
}
};
// A special separator that only applies to GameWindows in the
// Window menu.
// This is initialized further in the constructor.
private static WindowsMenuCommandSeparator separatorItem
= new WindowsMenuCommandSeparator(11, null);
// Game menu
private Menu gameMenu = menuBar.generate("Game");
private Menu handicapMenu = mListen.generateMenu(gameMenu, "Place Handicap");
private MenuItem passItem = mListen.generate(gameMenu, "Pass");
private MenuItem undoItem = mListen.generateWithSep(gameMenu, "Undo");
private MenuItem doneItem = mListen.generate(gameMenu, "Done");
private MenuItem adjournItem = mListen.generate(gameMenu, "Adjourn");
private MenuItem resignItem = mListen.generate(gameMenu, "Resign");
private CheckboxMenuItem localScoringItem
= cListen.generateWithSep(gameMenu, "Calculate Score", true);
// Variations menu
private Menu variationsMenu = menuBar.generate("Variations");
// Debug menu
private Menu debugMenu = menuBar.generate("Debug");
private MenuItem dumpBoardItem = mListen.generate(debugMenu, "Dump Board");
private MenuItem dumpGroupsItem = mListen.generate(debugMenu, "Dump Groups");
// Option Strings
private String gwinTermOutString = "Game Window Show Terminal Output";
private String savePromptString = "Game Window prompt for save";
private String NcoordString = "North Board Coordinates";
private String WcoordString = "West Board Coodinates";
private String EcoordString = "East Board Coordinates";
private String ScoordString = "South Board Coordinates";
private String statusString = "Game Window Show Game Status";
private String kibitzString = "Game Window Show Kibitz Area";
// ---*** Is the following redundant with GlobalOptions.variationString???
private String variationString = "Game Window Show Variation Letters";
private String bsizeString = "Game Window Size";
private String sizeString; // initialised in init().
String getSizeString() {return sizeString;}
private static Boolean T = new Boolean(true);
private static Boolean F = new Boolean(false);
// init() below expresses ownership.
/** A class of panel that sets its background to the game window's
* background and listens to various events. */
class GWPanel extends Panel implements Optionizable {
GWPanel () {
super();
try {
opser.expressOwnership(GlobalOptions.gwinColorString,
Optionizer.TYPE_COLOR, this,
new ColorSpec(gwinDefaultBackground));
setBackground(opser.getColorOption(GlobalOptions.gwinColorString));
}
catch (Exception e) {
Debug.backtrace(e);
}
}
public void optionEvent (String key, Object value) {
if (opser.isSameKey(key, GlobalOptions.gwinColorString)) {
setBackground(opser.getColorOption(GlobalOptions.gwinColorString));
repaint();
}
}
} // end inner class GWPanel
/****************************
* Initialization *
****************************/
// Make a server game
public GameWindow (int size, String white, String wrank, String black,
String brank, double komi, TerminalWindow term,
ServerConnection sc, int gnum, int matchi, int byotime,
boolean freeGame) throws ErgoException {
window = term;
conn = sc;
game = new Game(this, size, white, wrank, black, brank, komi, byotime,
freeGame, (conn == null ? null : conn.server));
gameNumber = gnum;
browsing = false;
isParticipating = matchi != -1;
// +++ should probably use Move.NONE here instead of -1
ourColor = matchi; // Move.WHITE, Move.BLACK, Move.BOTH, or -1
init();
}
// Make a local game
public GameWindow (int size, String white, String wrank,
String black, String brank, double komi,
TerminalWindow term) throws ErgoException {
this(size, white, wrank, black, brank, komi, term, null, -1, -1, -1, true);
}
// Make a game window from an already constructed game. Used by SGF reader.
public GameWindow (Game g, TerminalWindow term) throws ErgoException {
window = term;
game = g;
gameNumber = -1;
browsing = false;
isParticipating = false;
init();
}
// For the Optionizable Interface
public void optionEvent (String keyword, Object value) {
if (opser.isSameKey(keyword, statusString))
statusPanel = configureStatus( (String)value);
else if (opser.isSameKey(keyword, kibitzString))
setKibitzVisible( ((Boolean)value).booleanValue());
else if (TextColorManager.isColorString(keyword))
kibitzArea.setColorVec(TextColorManager.getColorVec());
else if (opser.isSameKey(keyword, GlobalOptions.textfontString))
kibitzArea.setFont(opser.getFontOption(GlobalOptions.textfontString));
else if (opser.isSameKey(keyword, GlobalOptions.gwinColorString)) {
setBackground(opser.getColorOption(GlobalOptions.gwinColorString));
repaint();
}
}
class SizeListener extends ComponentAdapter {
public void componentResized(ComponentEvent e) {
Dimension s = getSize();
opser.updateOption(getSizeString(), s);
}
}
// Initialize everything.
private void init () {
popupControl = new PopupController(this, "Options");
popupHandler = new PopupHandler();
separatorItem.appliesTo = this.getClass();
setBrowsing(false);
/***
if (!isLocalGame()) {
browseButton = new Button(" Browse "); // Should be ImageButton eventually
browseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
toggleBrowsingMode();
}
});
}
***/
stepBackButton = makeImageButton("back.gif");
stepForwardButton = makeImageButton("fwd.gif");
//jumpBackButton = makeImageButton("backfast.gif");
//jumpForwardButton = makeImageButton("fwdfast.gif");
toBeginningButton = makeImageButton("backstart.gif");
toEndButton = makeImageButton("fwdend.gif");
fwdKibButton = makeImageButton("fwdkib.gif");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -