📄 terminalwindow.java
字号:
package ergo.ui;
// $Id: TerminalWindow.java,v 1.8 1999/08/15 01:40:29 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.Util;
import ergo.util.Debug;
import ergo.util.ErgoException;
import ergo.util.ParsedMessage;
import ergo.util.ParseException;
import ergo.util.Position;
import ergo.util.CommandHistory;
import ergo.server.GoServer;
import ergo.server.ServerConnection;
import ergo.logic.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Vector;
import java.util.Enumeration;
import nom.rb.common.*;
/** WindowRegistrar maintains a set of menus. Each menu is associated
* with a given Frame. Each menu item implements the WindowsMenuCommand
* interface. Menu items are added to a given menu only if they apply
* to that menu's associated Frame.
*
* We should really have some higher level notion of command tables
* that map to menu items and/or keystrokes.
*
* There is only ever one instance of this class.
*/
class WindowRegistrar {
// menus and frames must always be kept in synch.
private Vector menus = new Vector(); // Menu objects only. no nulls.
private Vector frames = new Vector(); // Frame objects only. no nulls.
// Map from menu to vector of WindowsMenuCommands.
// The vector of menu commands and the items in the corresponding
// menu must always be kept in synch.
private java.util.Hashtable map = new java.util.Hashtable();
private WindowsMenuCommandSeparator separator = new WindowsMenuCommandSeparator(998, null);
private Menu menuAt (int i) {
return (Menu) menus.elementAt(i);
}
private Frame frameAt (int i) {
return (Frame) frames.elementAt(i);
}
// Return the index of o in v, or -1 if not found. Test with ==.
private int find (Vector v, Object o) {
for (int i = 0; i < v.size(); i++) {
if (v.elementAt(i) == o)
return i;
}
return -1;
}
// Iterate over all the menus and add this command if it applies
// to the Frame type associated with that window and if it's not
// already in them.
public synchronized void registerWindowsMenuCommand (WindowsMenuCommand cmd) {
for (int i = 0; i < menus.size(); i++) {
Menu menu = menuAt(i);
Frame frame = frameAt(i);
if (cmd.appliesTo() == null || cmd.appliesTo() == frame.getClass()) {
registerWindowsMenuCommandInternal(cmd, menu, frame);
}
}
}
// Add a command to the given menu if it's not already present.
// Add it at the correct priority.
private void registerWindowsMenuCommandInternal (WindowsMenuCommand cmd, Menu menu,
Frame frame) {
Vector menuCommands = (Vector) map.get(menu);
if (find(menuCommands, cmd) != -1)
return; // do not duplicate commands
int index = 0;
for (int i = 0; i < menuCommands.size(); ++i) {
WindowsMenuCommand inMenu = (WindowsMenuCommand) menuCommands.elementAt(i);
if (cmd.menuPriority() < inMenu.menuPriority())
break;
else
++index;
}
// all game windows have equal high priority, if we get here, i==size()....
// Debug.println("Adding "+cmd.menuString()+" at "+i);
if (cmd instanceof GameWindow
&& index > 0
&& !(menuCommands.elementAt(index - 1) instanceof WindowsMenuCommandSeparator)
&& !(menuCommands.elementAt(index - 1) instanceof GameWindow)) {
menu.insertSeparator(index);
menuCommands.insertElementAt(separator, index);
++index;
}
menuCommands.insertElementAt(cmd, index);
if (cmd.menuString() == null)
menu.insertSeparator(index);
else {
java.awt.MenuItem toadd = ((cmd.menuShortcut() == null)
? new java.awt.MenuItem(cmd.menuString())
: new java.awt.MenuItem(cmd.menuString(), cmd.menuShortcut()));
toadd.addActionListener(new WindowsMenuCommandActionListener(cmd, frame));
menu.insert(toadd, index);
}
}
// Remove the command from each menu that contains it,
// and from the associated vector of commands.
public void deregisterWindowsMenuCommand (WindowsMenuCommand cmd) {
for (int i = 0; i < menus.size(); i++) {
Menu menu = menuAt(i);
Vector commands = (Vector) map.get(menu);
int index = find(commands, cmd);
if (index != -1) {
commands.removeElementAt(index);
menu.remove(index); // corresponding element in menu
// Remove a separator above this command, if any.
if (index > 0
&& commands.elementAt(index - 1) instanceof WindowsMenuCommandSeparator
&& cmd instanceof GameWindow
// ...and the following item isn't a GameWindow.
// Note that index now points to the following item.
&& (index >= commands.size()
|| !(commands.elementAt(index) instanceof GameWindow))) {
commands.removeElementAt(index - 1);
menu.remove(index - 1);
}
}
}
}
public void updateWindowTitle (WindowsMenuCommand cmd) {
for (int i = 0; i < menus.size(); i++) {
Menu menu = menuAt(i);
Vector v = (Vector) map.get(menuAt(i));
if (v != null) {
int index = find(v, cmd);
if (index != -1) {
MenuItem item = menu.getItem(index);
if (item != null)
item.setLabel(cmd.menuString());
}
}
}
}
// Compiler goes into infinite loop if this is anonymous at 1.1.1.
// It looked very cool, tho.
class WindowsMenuCommandActionListener implements ActionListener {
WindowsMenuCommand cmd;
Frame frame;
WindowsMenuCommandActionListener (WindowsMenuCommand r1, Frame f) {
cmd = r1;
frame = f;
}
public void actionPerformed (ActionEvent e) {
cmd.menuSelect(frame);
}
}
// Add all the same items to a menu as are in another menu that belongs
// to the same type of frame.
private void menuManufac (Menu menu, Frame frame) {
// Try to find another menu that corresponds to the same kind of frame.
if (menus.size() > 0) { // otherwise nothing to copy
Menu m = null;
for (int i = 0; i < menus.size(); ++i) {
Frame f = frameAt(i);
// Note we don't want to use the menu we're trying to populate.
if (menuAt(i) != menu && f.getClass() == frame.getClass()) {
m = menuAt(i);
break;
}
}
if (m == null)
m = menuAt(0); // Just use the first one. Should work for now.
Vector v = (Vector) map.get(m);
if (v != null) {
for (int j = 0; j < v.size(); j++) {
WindowsMenuCommand cmd = (WindowsMenuCommand) v.elementAt(j);
// This actually goes through all the hoops of registering this
// command with all the menus, but it should only add it to the
// new menu since the others already contain this command.
if (cmd != null)
registerWindowsMenuCommand(cmd);
}
}
}
}
public Menu registerMenu (Menu menu, Frame parent) {
menus.addElement(menu);
frames.addElement(parent);
map.put(menu, new Vector());
menuManufac(menu, parent);
return menu;
}
public void releaseMenu (Menu togo) {
for (int i = 0; i < menus.size(); i++) {
if (menuAt(i) == togo) {
menus.removeElementAt(i);
frames.removeElementAt(i);
}
}
}
} // end class WindowRegistrar
/**
* TerminalWindow is the top-level window for user input, chatting,
* and miscellaneous output. A major function of the TerminalWindow
* is to implement the GoClient interface.
*/
public class TerminalWindow extends Frame
implements Runnable, Focusable, WindowsMenuCommand, Optionizable,
GoClient, PopupContributor {
// +++ These need to be optionized.
public static Color defaultColor = Color.black;
public static Color tellColor = Color.magenta;
public static Color yellColor = Color.blue;
public static Color shoutColor = Color.black;
// kibitzColor is also used for "say" output.
public static Color kibitzColor = new Color(0, 100, 0); // dark green.
public static Color warningColor = Color.black;
public static Color commandColor = Color.red;
private PopupController popupControl;
private PopupHandler popupHandler;
private CommandHistory commandHistory = new CommandHistory();
public static WindowRegistrar registrar = new WindowRegistrar();
private Vector focusOrder = null; // For the Focusable interface...
private int focusIndex = 0;
private String accountName = null;
private String rank = null;
public Ergo ergo = null;
public ServerConnection conn; // required for title of info windows
private Vector gameWindows = new Vector();
private Vector otherWindows = new Vector();
private ObserveDialog observeDialog;
private String defaultDirectory = null; // where to load/save SGF files.
// This is part of TerminalWindow because it should retain defaults
// throughout the life of the Ergo app, but it's public since
// the Controller is the one that uses it most.
private LoginDialog loginDialog = null;
// 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.
// ---*** The above comment is still somewhat true, but the menu items are
// being replaced by Command objects so the strings will only be in
// the Command anyway. -sigue July 23, 1999
private urMenuBar menuBar;
// File menu
private Menu goConnectSubmenu;
private MenuItem newServerItem;
private MenuItem disconnectItem;
private MenuItem saveOutputItem;
private MenuItem clearOutputItem;
private MenuItem loadItem;
// Go menu
private MenuItem observeItem;
private MenuItem matchItem;
private MenuItem automatchItem;
private MenuItem localGameItem;
// Options menu
private CheckboxMenuItem soundItem;
private MenuItem saveOptionsItem;
// Debug menu
private Menu debugMenu;
private CheckboxMenuItem debugItem;
private CheckboxMenuItem logicItem;
private CheckboxMenuItem rawItem;
private boolean rawMode;
// Windows menu
private Menu windowsMenu;
// Help menu
private Menu helpMenu;
private MenuItem aboutItem;
private MenuItem docItem;
public TextField inputField;
public TextArea mainArea;
public TextArea messageArea;
// ini Keys for server.
private String serverString = "Server";
private String widthString = "Main window width"; // default: 80
private String heightString = "Main area height"; // default: 24
private String mheightString = "Message area height"; // default: 7
private String exitString = "Save Options on Exit"; // default: true
private String mainposString = "Main Window Position"; // default: 0, 0
private boolean soundEnabled;
private Optionizer opser = Ergo.opser; // bit silly.
/****************************
* Initialization *
****************************/
// Note that by the time this window is created the init file has been loaded.
public TerminalWindow (Ergo ergo, GoServer server, boolean connect) {
init_options();
init_menus();
conn = new ServerConnection(this);
if (connect && server != null)
conn.open(server);
this.ergo = ergo;
Debug.setDebug(opser.getBooleanOption(GlobalOptions.debugmodeString));
popupControl = new PopupController(this, "Options");
popupHandler = new PopupHandler();
populateServers();
if (server != null)
goConnectSubmenu.add(new GoServerMenuItem(server));
int width = opser.getIntegerOption(widthString);
inputField = new TextField(width);
mainArea = new TextArea(opser.getIntegerOption(heightString), width, null, true);
messageArea = new TextArea(opser.getIntegerOption(mheightString), width);
inputField.addActionListener(new ActionListener () {
public void actionPerformed (ActionEvent e) {
// The user has pressed Return in the inputField...
String text = inputField.getText();
inputField.setText("");
processCommand(text.trim(), null);
}
});
addWindowListener(new WindowAdapter () {
public void windowClosing (WindowEvent e) {
exit();
}
});
// Set the focus to inputField when the window is initially shown.
addComponentListener(new ComponentAdapter () {
public void componentShown (ComponentEvent e) {
// Putting a little delay here seems to make this work more often than not.
// I wonder if there's a better way... 200ms shouldn't be too noticable...
try {
Thread.sleep(200);
} catch (InterruptedException ie) {}
inputField.requestFocus();
}
});
// Make arrow keys get Next/Previous input history item.
inputField.addKeyListener(new KeyAdapter() {
public void keyPressed (KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
String prev = commandHistory.getPreviousElement();
if (prev != null) {
inputField.setText(prev);
inputField.setCaretPosition(prev.length());
}
break;
case KeyEvent.VK_DOWN:
String next = commandHistory.getNextElement();
if (next != null) {
inputField.setText(next);
inputField.setCaretPosition(next.length());
}
break;
}
}
});
// We begin in a disconnected state.
if (conn == null || !conn.isConnected())
noteDisconnected();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -