Package jchessboard

Source Code of jchessboard.JChessBoard$PGNFileFilter

/* JChessBoard -- a chess game
* Copyright (C) 2000-2004 Claus Divossen <claus.divossen@gmx.de>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/* $Id: JChessBoard.java 7 2009-11-15 18:58:42Z cdivossen $ */

package jchessboard;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

/**
* JChessBoard is the main class. It does the following things:
* - manage the gui
* - create the other class instances: AI, VirtualBoard (the main baord)
*     VisualBoard, BoardConnector, ConnectionIndicator, ConnectionListener
* - dispatch the moves and other events to the appropriate recipients
* All communication between the other classes will be done through
* JChessBoard, or using its public methods and attributes.
* @see AI
* @see VirtualBoard
* @see VisualBoard
* @see ConnectionListener
* @see BoardConnector
* @see Protocol
* @see ConnectionIndicator
* @see Move
* @see PGN
* @see GameTable
* @see GameNode
* @see Notation
* @see History
* @see InfoPanel
* @see Chat
* @see BoardEditor
* @see ChessClock
* @see Settings
* */

public class JChessBoard extends JFrame {
  static final String TITLE = "JChessBoard v1.5.1";
  static final String COPYRIGHT_MESSAGE =
    "JChessBoard -- a chess game\n"
    + "Copyright (C) 2000 Claus Divossen <claus.divossen\u0040gmx.de>\n"
    + "\n"
    + "This program is free software; you can redistribute it and/or modify\n"
    + "it under the terms of the GNU General Public License as published by\n"
    + "the Free Software Foundation; either version 2 of the License, or\n"
    + "(at your option) any later version.\n"
    + "\n"
    + "This program is distributed in the hope that it will be useful,\n"
    + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    + "GNU General Public License for more details.\n"
    + "\n"
    + "You should have received a copy of the GNU General Public License\n"
    + "along with this program; if not, write to the Free Software\n"
    + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
  static final int MIN_WIDTH = 350;
  static final int MIN_HEIGHT = 471;
  final static String HUMAN = "Human";
  final static String COMPUTER = "Computer";
  final static String PEER = "Peer";
  final static String UNKNOWN = "-----";
  VisualBoard visualBoard;
  ConnectionIndicator connectionIndicator;
  BoardConnector boardConnector;
  AI ai;
  ChessClock chessClock;
  Chat chat;
  String whitePlayer = HUMAN;
  String blackPlayer = COMPUTER;
  History history;
  static java.util.List windowList = new Vector();
  // Menu items that need to be known:
  private JMenuItem menuItemNewGame;
  private JMenuItem menuItemLoadFile;
  private JMenuItem menuItemSave;
  private JMenuItem menuItemSaveAs;
  private JMenuItem menuItemUploadGame;
  private JMenuItem menuItemOfferDraw;
  private JMenuItem menuItemResign;
  private JMenuItem menuItemSwitchSides;
  private JMenuItem menuItemConnect;
  private JMenuItem menuItemCloseConnection;
  private JMenuItem about;
  private JMenuItem help;
  private JCheckBoxMenuItem clockCheckbox;
  private JCheckBoxMenuItem enableBeep;
  private JCheckBoxMenuItem reverseBoard;
  private JCheckBoxMenuItem showPossibleMoves;
  //    private JCheckBoxMenuItem whiteIsAI;
  //    private JCheckBoxMenuItem blackIsAI;
  private JMenuItem undoMove;
  // ConnectionListener is the connection server
  private static ConnectionListener connectionListener;
  private String otherHostname = "";
  private static String fileChooserDir;
  private String fileName = "";
  private boolean fileIsChanged = false;
  private final static String settingsFileName = "JChessBoard.conf";
  Settings settings;
  private JPanel buttonPanel;
  private JButton buttonPrevious, buttonNext, buttonStart, buttonEnd;
  private boolean gameIsFinished = false;
  // Reguired for the timeout selector:
  JSlider whiteSlider, blackSlider;
  long newWhiteTime, newBlackTime;
  JLabel whiteTimeoutLabel, blackTimeoutLabel;
  boolean coupleSliders;
  JCheckBox coupleSlidersCheckBox;
  Protocol protocol = new Protocol(this);
  GameTable gameTable = null;
  int currentGameIndex = 0;
  private InfoPanel infoPanel;
  private JTabbedPane tabbedPane;
  private Notation notation;

  public static String getVersion() {
    return "$Id: JChessBoard.java 7 2009-11-15 18:58:42Z cdivossen $";
  }

  static class PGNFileFilter extends javax.swing.filechooser.FileFilter {
    public String getDescription() {
      return "PGN Files (*.pgn)";
    }
    public boolean accept(java.io.File file) {
      return file.exists() && file.canRead() && (file.getName().endsWith(".pgn") || file.isDirectory());
    }
  }

  /**
   * @return
   */
  public BoardConnector getBoardConnector() {
    return boardConnector;
  }

  /**
   * @param connector
   */
  public void setBoardConnector(BoardConnector connector) {
    boardConnector = connector;
  }

  /**
   * Test whether this board is currently connected.
   */
  public boolean isConnected() {
    return (protocol.isConnected() && (boardConnector != null));
  }

  public void setEnableClock(boolean enable) {
    clockCheckbox.setSelected(enable);
    chessClock.setEnabled(enable);
    settings.enableClock = enable;
  }

  public boolean getEnableClock() {
    return clockCheckbox.isSelected();
  }

  public void connectionClosed() {
    // Protocol stuff
    protocol.connectionClosed();
    // Visual stuff
    chessClock.stopClock();
    connectionIndicator.setNoConnection();
    showMessage("Connection closed.");
    gameTable.setEnabled(true);
    setEnableClock(false);
    gameTable.setNetworkGameIndex(-1);
    if (fileName != null && fileName.length() > 0)
      setTitle(TITLE + " - " + fileName);
    else
      setTitle(TITLE);
    beep();
    whitePlayer = HUMAN;
    blackPlayer = HUMAN;
    boardConnector = null;
    prepareMove();
    // --JAM: Switch the board around.  If Black is disconnected at the
    // beginning of the game, the board is still oriented for Black.
    // It's disconcerting.
  }

  /**
   * Performs all the steps necessary to begin playing a connected game.
   * This includes setting the chess clocks, clearing the connection
   * indicator, starting a new game, resetting the title, and setting
   * variables to allow the first mover to be White.
   */
  public void connectionEstablished() {
    protocol.connectionEstablished();
    connectionIndicator.setReady();
    setEnableClock(true);
    settings.whiteTime = 600000;
    settings.blackTime = 600000;
    newGame();
    gameTable.setNetworkGameIndex(gameTable.getCurrentGameIndex());
    protocol.sendPlayerName(settings.userName);
    if (fileName != null && fileName.length() > 0)
      setTitle(TITLE + " - Connected with \"" + otherHostname + "\" - " + fileName);
    else
      setTitle(TITLE + " - Connected with \"" + otherHostname + "\"");
    beep();
    showMessage("Connected.");
    showMessage("Your are now playing with \"" + otherHostname + "\".");
    showMessage("Either player can make a move. The first move decides who plays which side.");
    showMessage("You can set your chat name with /name <your name>");
    prepareMove();
  }

  public void updateNavigationButtons() {
    // Activate/deactivate navigation buttons:
    boolean hasPrev;
    if (history.getPreviousGameNode() == history.getCurrentGameNode()) {
      buttonStart.setEnabled(false);
      buttonPrevious.setEnabled(false);
      hasPrev = false;
    } else {
      buttonStart.setEnabled(true);
      buttonPrevious.setEnabled(true);
      hasPrev = true;
    }
    if (history.getNextGameNode() == history.getCurrentGameNode()) {
      buttonNext.setEnabled(false);
      buttonEnd.setEnabled(false);
    } else {
      buttonNext.setEnabled(true);
      buttonEnd.setEnabled(true);
    }
  }

  public VirtualBoard getCurrentVirtualBoard() {
    return history.getCurrentBoard();
  }

  public boolean isWhiteTurn() {
    return history.getCurrentBoard().isWhiteTurn();
  }

  /**
   * Executes the move your opponent has done.
   * This is an interface method used by Protocol.
   */
  public void makePeersMove(Move move) {
    chessClock.stopClock();
    beep();
    gotoGame(gameTable.getNetworkGameIndex());
    history.gotoLast();
    // The first move decides!
    if (whitePlayer.equals(UNKNOWN) || blackPlayer == UNKNOWN) {
      if (isWhiteTurn()) {
        whitePlayer = PEER;
        blackPlayer = HUMAN;
        visualBoard.setReverseBoard(true);
        reverseBoard.setSelected(true);
      } else {
        whitePlayer = HUMAN;
        blackPlayer = PEER;
      }
    }
    updateSTR();
    VirtualBoard virtualBoard = getCurrentVirtualBoard().clonedBoard();
    String algMove = virtualBoard.algebraic(move);
    if (isConnected())
      showMessage("Opponents move: " + algMove);
    try {
      virtualBoard.makeMove(move);
      history.addMove(algMove, virtualBoard);
      visualBoard.showMove(move, 1000);
      chessClock.startClock();
      checkFinish();
    } catch (VirtualBoard.ImpossibleMoveException e) {
      showMessage("Error parsing opponents move: " + move);
      protocol.sendError("Error parsing move: " + move);
    }
    prepareMove();
  }

  public void makeBatchMove(Move move) {
    VirtualBoard virtualBoard = getCurrentVirtualBoard().clonedBoard();
    String algMove = virtualBoard.algebraic(move);
    try {
      virtualBoard.makeMove(move);
    } catch (VirtualBoard.ImpossibleMoveException e) {
      showMessage("Error parsing batch move: " + move);
    }
    history.addMove(algMove, virtualBoard);
  }

  /**
   * Executes the move of the AI.
   * This is an interface method for the AI.
   */
  public void makeAIsMove(Move move) {
    chessClock.stopClock();
    VirtualBoard virtualBoard = getCurrentVirtualBoard().clonedBoard();
    String algMove = virtualBoard.algebraic(move);
    showMessage("AI's move: " + algMove);
    try {
      virtualBoard.makeMove(move);
      history.addMove(algMove, virtualBoard);
      visualBoard.showMove(move, 1000);
      chessClock.startClock();
      triggerAI();
      checkFinish();
    } catch (VirtualBoard.ImpossibleMoveException e) {
    }
  }

  /**
   * Executes the users move.
   * This is an interface method for VisualBoard.
   */
  public void makeUsersMove(Move move) {
    chessClock.stopClock();
    VirtualBoard virtualBoard = getCurrentVirtualBoard().clonedBoard();
    String algMove = virtualBoard.algebraic(move);
    if (isConnected())
      showMessage("Your move: " + algMove);
    try {
      virtualBoard.makeMove(move);
    } catch (VirtualBoard.ImpossibleMoveException e) {
    }
    history.addMove(algMove, virtualBoard);
    if (isConnected() && gameTable.getCurrentGameIndex() == gameTable.getNetworkGameIndex()) {
      protocol.sendMove(move);
      // The first move decides!
      if (whitePlayer.equals(UNKNOWN) || blackPlayer == UNKNOWN) {
        if (isWhiteTurn()) {
          whitePlayer = PEER;
          blackPlayer = HUMAN;
          visualBoard.setReverseBoard(true);
          reverseBoard.setSelected(true);
        } else {
          whitePlayer = HUMAN;
          blackPlayer = PEER;
        }
      }
      updateSTR();
      if (getEnableClock()) {
        if (virtualBoard.isWhiteTurn())
          protocol.sendPlayersTime(chessClock.getBlackTime());
        else
          protocol.sendPlayersTime(chessClock.getWhiteTime());
      }
    }
    chessClock.startClock();
    triggerAI();
    checkFinish();
  }

  public void triggerAI() {
    if (!(whitePlayer == COMPUTER) && !(blackPlayer == COMPUTER))
      return;
    if (((whitePlayer == COMPUTER) || (blackPlayer == COMPUTER)) && ai == null) {
      ai = new AI(this);
    }
    if (ai != null && !(whitePlayer == COMPUTER) && !(blackPlayer == COMPUTER)) {
      ai.shutdown();
      ai = null;
      return;
    }
    if (isWhiteTurn()) {
      if (whitePlayer == COMPUTER) {
        long timeout;
        if (clockCheckbox.isSelected()) {
          if (history.getFullMoveNumber() < 40)
            timeout = chessClock.getWhiteTime() / ((long) (60 - history.getFullMoveNumber()));
          else
            timeout = chessClock.getWhiteTime() / 20L;
          if (timeout < 500) // Minimum time: 500ms.
          timeout = 500;
        } else
          timeout = 5000; // Fixed time, 5 sec.
          ai.prepareMove(timeout);
      }
    } else {
      if (blackPlayer == COMPUTER) {
        long timeout;
        if (clockCheckbox.isSelected()) {
          if (history.getFullMoveNumber() < 40)
            timeout = chessClock.getBlackTime() / ((long) (60 - history.getFullMoveNumber()));
          else
            timeout = chessClock.getBlackTime() / 20;
          if (timeout < 500) // Minimum time: 500ms.
          timeout = 500;
        } else
          timeout = 5000; // Fixed time, 5 sec.
        ai.prepareMove(timeout);
      }
    }
  }

  /**
   * Pepares some settings for the next move.
   * Mainly, it starts and stops the chess clocks.
   */
  public void prepareMove() {
    // Create the appropiate setting to receive the next move.
    // Lock/Unlock the visualBoard:
    //      if (gameIsFinished)
    //        visualBoard.setLocked(true);
    //   else
    visualBoard.setLocked(false);
    if (isConnected() && history.getNextGameNode() != history.getCurrentGameNode())
      visualBoard.setLocked(true);
    if (isConnected() && (gameTable.getCurrentGameIndex() != gameTable.getNetworkGameIndex()))
      visualBoard.setLocked(true);
    if (isWhiteTurn() && ((whitePlayer == PEER) || (whitePlayer == COMPUTER)))
      visualBoard.setLocked(true);
    if (!isWhiteTurn() && ((blackPlayer == PEER) || (blackPlayer == COMPUTER)))
      visualBoard.setLocked(true);
    updateMenu();
    chessClock.updatePlayerSelectors();
  }

  public void switchSides() {
    String tmp = whitePlayer;
    whitePlayer = blackPlayer;
    blackPlayer = tmp;
    reverseBoard.setSelected(!reverseBoard.isSelected());
    visualBoard.setReverseBoard(reverseBoard.isSelected());
    updateSTR();
    showMessage("Sides switched.");
    prepareMove();
  }

  public void updateSTR() {
    PGN.STR str = gameTable.getSTR(gameTable.getCurrentGameIndex());
    str.setTag("White", "?");
    str.setTag("Black", "?");
    if (whitePlayer.equals(PEER))
      str.setTag("White", protocol.getOtherPlayersName());
    if (whitePlayer.equals(HUMAN))
      str.setTag("White", settings.userName);
    if (whitePlayer.equals(COMPUTER))
      str.setTag("White", TITLE);
    if (blackPlayer.equals(PEER))
      str.setTag("Black", protocol.getOtherPlayersName());
    if (blackPlayer.equals(HUMAN))
      str.setTag("Black", settings.userName);
    if (blackPlayer.equals(COMPUTER))
      str.setTag("Black", TITLE);
    infoPanel.repaint();
    gameTable.repaint();
  }

  /*
   * Stops the game and creates the appropriate message if the game comes out
   * to be finished.
   */
  public void checkFinish() {
    VirtualBoard virtualBoard = history.getCurrentBoard();
    if (virtualBoard.gameIsFinished()) {
      if (virtualBoard.onlyKingsLeft()) {
        showMessage("Draw!");
      } else if (virtualBoard.isWhiteTurn()) {
        if (virtualBoard.isAttackedByBlack(virtualBoard.getWhiteKingPos())) {
          Object[] options = { "Accept", "Reject" };
          showMessage("Black wins!");
          JOptionPane.showMessageDialog(null, "Black wins!");
        } else {
          showMessage("Draw (stalemate)!");
          JOptionPane.showMessageDialog(null, "Draw!");
        }
      } else {
        if (virtualBoard.isAttackedByWhite(virtualBoard.getBlackKingPos())) {
          showMessage("White wins!");
          JOptionPane.showMessageDialog(null, "White wins!");
        } else {
          showMessage("Draw (stalemate)!");
          JOptionPane.showMessageDialog(null, "Draw!");
        }
      }
      chessClock.stopClock();
    } else if (clockCheckbox.isSelected() && (chessClock.getWhiteTime() <= 0 || chessClock.getBlackTime() <= 0)) {
      chessClock.stopClock();
      if (chessClock.getWhiteTime() <= 0) {
        showMessage("Black wins (time forfeit)!");
        JOptionPane.showMessageDialog(null, "Black wins!");
      } else {
        showMessage("White wins (time forfeit)!");
        JOptionPane.showMessageDialog(null, "White wins!");
      }
      showMessage(
          "White: "
          + chessClock.formatTime(chessClock.getWhiteTime())
          + ", Black: "
          + chessClock.formatTime(chessClock.getBlackTime()));
      setEnableClock(false);
      showMessage("The clocks have been disabled, you can continue playing, if you want to.");
    } else
      prepareMove();
  }

  public void newGame() {
    PGN.STR str = new PGN.STR();
    gotoGame(gameTable.addGame("", str));
    visualBoard.setReverseBoard(false);
    if (isConnected()) {
      whitePlayer = UNKNOWN;
      blackPlayer = UNKNOWN;
      gameTable.setNetworkGameIndex(gameTable.getCurrentGameIndex());
    } else {
      whitePlayer = HUMAN;
      blackPlayer = COMPUTER;
    }
    Calendar cal = Calendar.getInstance();
    str.setTag(
        "Date",
        cal.get(Calendar.YEAR) + "." + (cal.get(Calendar.MONTH) + 1) + "." + cal.get(Calendar.DAY_OF_MONTH));
    chessClock.updatePlayerSelectors();
    updateSTR();
    if (ai != null)
      ai.newGame();
    chessClock.stopClock();
    chessClock.resetClocks();
    prepareMove();
    //            history.setResult("*");
    gameIsFinished = false;
    fileIsChanged = true;
  }

  public void newGame(VirtualBoard vb) {
    newGame();
    history.setBoard(vb.clonedBoard());
    PGN.STR str = gameTable.getSTR(gameTable.getCurrentGameIndex());
    str.setTag("SetUp", "1");
    str.setTag("FEN", vb.getFEN());
    update();
  }

  /**
   * Removes this window and whats used by it and calls System.exit() if
   * there is no window left.
   */
  public void exit() {
    if (isConnected()) {
      int choice =
        JOptionPane.showConfirmDialog(null, "Are you sure?", "Close connection?", JOptionPane.YES_NO_OPTION);
      if (choice == JOptionPane.YES_OPTION) {
        boardConnector.closeConnection();
        connectionClosed();
      } else
        return;
    }
    if (isFileChanged())
      askForSaving();
    if (ai != null)
      ai.shutdown();
    try {
      dispose();
      finalize();
    } catch (Throwable t) {
      System.out.println(t);
    }
    System.gc();
    windowList.remove(this);
    if (windowList.size() == 0) {
      System.exit(0);
    }
  }

  /**
   * Enables/disables certain menu entries depending on the
   * current connection state. The checkbox menu items are
   * also updated.
   */
  public void updateMenu() {
    reverseBoard.setSelected(visualBoard.reverseBoard());
    if (isConnected()) {
      menuItemOfferDraw.setEnabled(true);
      menuItemResign.setEnabled(true);
      menuItemSwitchSides.setEnabled(true);
      menuItemUploadGame.setEnabled(true);
      menuItemCloseConnection.setEnabled(true);
      menuItemConnect.setEnabled(false);
    } else {
      menuItemUploadGame.setEnabled(false);
      menuItemOfferDraw.setEnabled(false);
      menuItemResign.setEnabled(false);
      menuItemSwitchSides.setEnabled(false);
      menuItemCloseConnection.setEnabled(false);
      menuItemConnect.setEnabled(true);
    }
  }

  /**
   * Takes back the last move. (What did you expect?)
   */
  public void undoMove() {
    history.removeLastMove();
    showMessage("Last move reversed.");
    // Take back two moves if playing against AI:
    if (((!isWhiteTurn() && blackPlayer == COMPUTER) || (isWhiteTurn() && whitePlayer == COMPUTER))
        && history.getFullMoveNumber() > 0) {
      history.removeLastMove();
    }
    gameIsFinished = false;
    prepareMove();
  }

  /**
   * Makes a beep, if enableBeep.isSelected().
   */
  public void beep() {
    if (enableBeep.isSelected()) {
      getToolkit().beep();
    }
  }

  public void showMessage(String message) {
    chat.showMessage(message);
  }
  public void showMessage(String message, String style) {
    chat.showMessage(message, style);
  }
  public void showReplaceableMessage(String message) {
    chat.showReplaceableMessage(message);
  }
  public void showReplaceableMessage(String message, String style) {
    chat.showReplaceableMessage(message, style);
  }

  public void timeForfeit() {
    if (isConnected() && whitePlayer == HUMAN)
      protocol.sendPlayersTime(chessClock.getWhiteTime());
    if (isConnected() && blackPlayer == HUMAN)
      protocol.sendPlayersTime(chessClock.getBlackTime());
    checkFinish();
  }

  /**
   * Creates a requester that allows to select time each player has to make moves.
   * If the board is connected, a confirmation request for this is send via the
   * BoardConnector. In that case, the clock will not yet be changed when this
   * method returns.
   */
  public void showTimeoutSelector() {
    Object message[] = new Object[6];
    message[0] = "Select the time each player has to make his moves:";
    newWhiteTime = settings.whiteTime;
    newBlackTime = settings.blackTime;
    coupleSlidersCheckBox = new JCheckBox("Couple sliders");
    coupleSlidersCheckBox.setSelected(settings.whiteTime == settings.blackTime);
    whiteTimeoutLabel = new JLabel("White: " + newWhiteTime / 60000 + " min.");
    whiteSlider = new JSlider(1, 60, (int) (newWhiteTime / 60000));
    whiteSlider.setPreferredSize(new Dimension(300, 40));
    whiteSlider.setPaintTicks(true);
    whiteSlider.setMajorTickSpacing(10);
    whiteSlider.setMinorTickSpacing(5);
    whiteSlider.setPaintLabels(false);
    whiteSlider.addChangeListener(new javax.swing.event.ChangeListener() {
      public void stateChanged(javax.swing.event.ChangeEvent e) {
        newWhiteTime = whiteSlider.getValue() * 60000L;
        whiteTimeoutLabel.setText("White: " + newWhiteTime / 60000 + " min.");
        if (coupleSlidersCheckBox.isSelected()) {
          newBlackTime = newWhiteTime;
          blackSlider.setValue((int) newBlackTime / 60000);
          blackTimeoutLabel.setText("Black: " + newBlackTime / 60000 + " min.");
        }
      }
    });
    blackTimeoutLabel = new JLabel("Black: " + newBlackTime / 60000 + " min.");
    blackSlider = new JSlider(1, 60, (int) (newBlackTime / 60000));
    blackSlider.setPreferredSize(new Dimension(300, 40));
    blackSlider.setPaintTicks(true);
    blackSlider.setMajorTickSpacing(10);
    blackSlider.setMinorTickSpacing(5);
    blackSlider.setPaintLabels(false);
    blackSlider.addChangeListener(new javax.swing.event.ChangeListener() {
      public void stateChanged(javax.swing.event.ChangeEvent e) {
        newBlackTime = blackSlider.getValue() * 60000L;
        blackTimeoutLabel.setText("Black: " + newBlackTime / 60000 + " min.");
        if (coupleSlidersCheckBox.isSelected()) {
          newWhiteTime = newBlackTime;
          whiteSlider.setValue((int) newWhiteTime / 60000);
          whiteTimeoutLabel.setText("White: " + newWhiteTime / 60000 + " min.");
        }
      }
    });
    message[1] = whiteSlider;
    message[2] = whiteTimeoutLabel;
    message[3] = blackSlider;
    message[4] = blackTimeoutLabel;
    message[5] = coupleSlidersCheckBox;
    Object options[] = { "OK", "Cancel" };
    int result =
      JOptionPane.showOptionDialog(
          null,
          message,
          "Set game time",
          0,
          JOptionPane.QUESTION_MESSAGE,
          null,
          options,
          options[0]);
    if (result == 0) {
      if (isConnected()) {
        showMessage("Requesting clock change...");
        protocol.requestClockChange(newWhiteTime, newBlackTime);
      } else {
        settings.whiteTime = newWhiteTime;
        settings.blackTime = newBlackTime;
        chessClock.resetClocks();
        prepareMove();
      }
    }
  }

  public void setClocks(long whiteTime, long blackTime) {
    settings.whiteTime = whiteTime;
    settings.blackTime = blackTime;
    chessClock.resetClocks();
    showMessage("Game time was changed.");
    prepareMove();
  }

  public void gotoGame(int newGameIndex) {
    if (currentGameIndex != -1 && history.isChanged()) {
      // Save the current game back to the list first.
      gameTable.setPGN(currentGameIndex, history.getPGNBody());
      fileIsChanged = true;
    }
    if (newGameIndex >= gameTable.getGameCount())
      newGameIndex = gameTable.getGameCount() - 1;
    int lastIndex = currentGameIndex;
    currentGameIndex = newGameIndex;
    gameTable.setSelectedIndex(currentGameIndex);
    gameTable.setCurrentGameIndex(currentGameIndex);
    gameTable.updateRow(lastIndex);
    try {
      history.loadGame(gameTable.getPGN(currentGameIndex), gameTable.getSTR(currentGameIndex));
    } catch (VirtualBoard.ImpossibleMoveException e) {
      showMessage(e.toString());
    }
    infoPanel.setSTR(gameTable.getSTR(currentGameIndex));
    history.gotoLast();
    update();
    prepareMove();
  }

  public void update() {
    visualBoard.update();
    gameTable.repaint();
    updateMenu();
    updateNavigationButtons();
    notation.update();
    chessClock.updatePlayerSelectors();
  }

  public boolean isFileChanged() {
    if (fileName == null || fileName.length() == 0)
      return false;
    if (fileIsChanged)
      return true;
    if (currentGameIndex != -1 && history.isChanged()) {
      fileIsChanged = true;
      return true;
    }
    return false;
  }

  public void loadFile(java.io.File file) {
    fileChooserDir = file.getPath();
    String netPGN = null;
    PGN.STR netSTR = null;
    if (isConnected()) {
      netPGN = gameTable.getPGN(gameTable.getNetworkGameIndex());
      netSTR = gameTable.getSTR(gameTable.getNetworkGameIndex());
    }
    history.clear();
    gameTable.clear();
    PGN.loadPGN(file, JChessBoard.this);
    if (isConnected()) {
      gameTable.setNetworkGameIndex(gameTable.addGame(netPGN, netSTR));
    }
    chessClock.resetClocks();
    chessClock.stopClock();
    whitePlayer = HUMAN;
    blackPlayer = HUMAN;
  }

  public void askForSaving() {
    int choice =
      JOptionPane.showConfirmDialog(
          null,
          fileName + " was changed. Save file?",
              "Save?",
                  JOptionPane.YES_NO_OPTION);
    if (choice == JOptionPane.YES_OPTION) {
      save(fileName, false);
    }
  }

  public void save(String fileName, boolean append) {
    if (currentGameIndex != -1 && history.isChanged()) {
      gameTable.setPGN(currentGameIndex, history.getPGNBody());
    }
    try {
      PGN.writePGN(new File(fileName), gameTable, append);
      showMessage("Game(s) saved to " + fileName + ".");
      fileIsChanged = false;
    } catch (IOException o) {
      showMessage("IOException: " + o);
    }
  }

  public void saveAs() {
    JFileChooser fileChooser = new JFileChooser();
    fileChooser.setCurrentDirectory(new java.io.File(fileChooserDir));
    fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
    fileChooser.resetChoosableFileFilters();
    fileChooser.addChoosableFileFilter(new PGNFileFilter());
    int choice = fileChooser.showSaveDialog(null);
    if (choice == JFileChooser.APPROVE_OPTION) {
      File file = fileChooser.getSelectedFile();
      fileChooserDir = file.getPath();
      fileName = file.getName();
      if (!fileName.endsWith(".pgn")) {
        fileName += ".pgn";
        file = new File(fileName);
      }
      setTitle(TITLE + " - " + fileName);
      if (file.exists()) {
        Object[] option = new Object[3];
        option[0] = "Append";
        option[1] = "Overwrite";
        option[2] = "Cancel";
        choice =
          JOptionPane.showOptionDialog(
              null,
              "File \"" + file.getName() + "\" exists.",
              "File exists",
              0,
              JOptionPane.QUESTION_MESSAGE,
              null,
              option,
              option[0]);
      }
      if (choice == 0) { // Append
        save(file.getName(), true);
        fileIsChanged = false;
        showMessage("Reloading complete file...");
        loadFile(file);
      } else if (choice == 1) { // Overwrite
        save(file.getName(), false);
      } else // Cancel
        return;
    }
  }

  public void uploadGame() {
    history.gotoLast();
    List allBoards = history.getAllBoards();
    protocol.startUpload();
    PGN.STR str = gameTable.getSTR(gameTable.getCurrentGameIndex());
    if (str.hasTag("SetUp"))
      protocol.initFromFEN(str.getTag("FEN"));
    if (allBoards.size() > 0) {
      for (int n = allBoards.size() - 1; n >= 0; n--) {
        Move move = ((VirtualBoard) allBoards.get(n)).getLastMove();
        if (move != null)
          protocol.sendBatchMove(move);
      }
    }
    protocol.uploadDone();
    whitePlayer = UNKNOWN;
    blackPlayer = UNKNOWN;
    showMessage("Upload finished");
    gameTable.setNetworkGameIndex(gameTable.getCurrentGameIndex());
    chessClock.stopClock();
    chessClock.resetClocks();
    update();
    prepareMove();
  }

  /**
   * The JChessBoard constructor.
   * @param title   The title of the created JFrame.
   */
  public JChessBoard(String title) {
    super(title);
    settings = new Settings(settingsFileName);
    boolean settingsHaveBeenLoaded = settings.read();
    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
    windowList.add(this);

    JSplitPane splitPane;
    splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    splitPane.setOneTouchExpandable(true);
    splitPane.setDividerLocation(150);

    //
    // GameTable
    //
    gameTable = new GameTable();
    //      gameTable.addGame("", new PGN.STR());

    final JPopupMenu gameTableContextMenu = new JPopupMenu();
    JMenuItem removeGameItem = new JMenuItem("Remove game(s)");
    removeGameItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        gotoGame(gameTable.removeSelectedGames());
        fileIsChanged = true;
      }
    });
    gameTableContextMenu.add(removeGameItem);

    gameTable.getJTable().addMouseListener(new MouseAdapter() {
      public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
          int index = gameTable.getSelectedIndex();
          gotoGame(index);
        }
      }
      public void mousePressed(MouseEvent e) {
        if (e.isPopupTrigger()) {
          gameTableContextMenu.show((Component) e.getSource(), e.getX(), e.getY());
        }
      }
      public void mouseReleased(MouseEvent e) {
        if (e.isPopupTrigger()) {
          gameTableContextMenu.show((Component) e.getSource(), e.getX(), e.getY());
        }
      }
    });

    KeyStroke enter = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ENTER, 0);
    gameTable.getJTable().unregisterKeyboardAction(enter);
    gameTable.getJTable().registerKeyboardAction(new ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent e) {
        synchronized (this) {
          gotoGame(gameTable.getSelectedIndex());
        }
      }
    }, enter, JComponent.WHEN_FOCUSED);
    KeyStroke del = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0);
    gameTable.getJTable().unregisterKeyboardAction(del);
    gameTable.getJTable().registerKeyboardAction(new ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent e) {
        gotoGame(gameTable.removeSelectedGames());
        fileIsChanged = true;
      }
    }, del, JComponent.WHEN_FOCUSED);

    splitPane.setTopComponent(gameTable);
    tabbedPane = new JTabbedPane();

    history = new History(this);
    infoPanel = new InfoPanel();
    notation = new Notation(history);

    tabbedPane.add("Notation", notation);
    tabbedPane.add("Tree", history);
    tabbedPane.add("Info", infoPanel);

    splitPane.setBottomComponent(tabbedPane);

    visualBoard = new VisualBoard(this);
    connectionIndicator = new ConnectionIndicator();
    chat = new Chat(this);
    boardConnector = null;

    if (connectionListener == null)
      connectionListener = new ConnectionListener();
    connectionListener.setPort(settings.networkPort);
    connectionListener.setEnabled(settings.enableServer);
    ai = null; // Because of memory consumption, the AI
    // will be started on demand.
    chessClock = new ChessClock(this);
    chessClock.setEnabled(settings.enableClock);
    fileChooserDir = System.getProperty("user.dir");

    // Button pane:
    buttonPanel = new JPanel(new GridLayout(1, 5));
    java.net.URL url = JChessBoard.class.getResource("/images/start.gif");
    ImageIcon icon = url != null ? new ImageIcon(url) : new ImageIcon("null");
    buttonStart = new JButton(icon);
    buttonStart.setContentAreaFilled(false);
    buttonStart.setMargin(new Insets(2, 2, 2, 2));
    buttonStart.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        history.gotoFirst();
      }
    });
    buttonPanel.add(buttonStart);

    url = JChessBoard.class.getResource("/images/prev.gif");
    icon = url != null ? new ImageIcon(url) : new ImageIcon("null");
    buttonPrevious = new JButton(icon);
    buttonPrevious.setContentAreaFilled(false);
    buttonPrevious.setMargin(new Insets(2, 2, 2, 2));
    buttonPrevious.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        history.prev();
      }
    });
    buttonPanel.add(buttonPrevious);

    url = JChessBoard.class.getResource("/images/next.gif");
    icon = url != null ? new ImageIcon(url) : new ImageIcon("null");
    buttonNext = new JButton(icon);
    buttonNext.setContentAreaFilled(false);
    buttonNext.setMargin(new Insets(2, 2, 2, 2));
    buttonNext.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        history.next();
      }
    });
    buttonPanel.add(buttonNext);

    url = JChessBoard.class.getResource("/images/end.gif");
    icon = url != null ? new ImageIcon(url) : new ImageIcon("null");
    buttonEnd = new JButton(icon);
    buttonEnd.setContentAreaFilled(false);
    buttonEnd.setMargin(new Insets(2, 2, 2, 2));
    buttonEnd.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        history.gotoLast();
      }
    });
    buttonPanel.add(buttonEnd);

    Container contentPane = getContentPane();
    GridBagLayout gridBag = new GridBagLayout();
    contentPane.setLayout(gridBag);

    GridBagConstraints c = new GridBagConstraints();

    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 1;
    c.gridheight = 1;
    c.weightx = 0;
    c.weighty = 0;
    c.fill = GridBagConstraints.HORIZONTAL;
    gridBag.setConstraints(chessClock, c);
    contentPane.add(chessClock);

    c.gridx = 0;
    c.gridy = 1;
    c.gridwidth = 1;
    c.gridheight = 1;
    c.weightx = 0;
    c.weighty = 0;
    c.fill = GridBagConstraints.NONE;
    gridBag.setConstraints(visualBoard, c);
    contentPane.add(visualBoard);

    c.gridx = 0;
    c.gridy = 2;
    c.gridwidth = 1;
    c.gridheight = 1;
    c.weightx = 0;
    c.weighty = 0;
    c.fill = GridBagConstraints.HORIZONTAL;
    gridBag.setConstraints(buttonPanel, c);
    contentPane.add(buttonPanel);

    c.gridx = 1;
    c.gridy = 0;
    c.gridwidth = GridBagConstraints.REMAINDER;
    c.gridheight = 3;
    c.weightx = 1;
    c.weighty = 0;
    c.fill = GridBagConstraints.BOTH;
    gridBag.setConstraints(splitPane, c);
    splitPane.setPreferredSize(new Dimension(210, 1));
    splitPane.setMinimumSize(new Dimension(210, 1));
    contentPane.add(splitPane);

    c.gridx = 0;
    c.gridy = 3;
    c.gridwidth = GridBagConstraints.REMAINDER;
    c.gridheight = GridBagConstraints.REMAINDER;
    c.weightx = 0;
    c.weighty = 1.0;
    c.fill = GridBagConstraints.BOTH;
    gridBag.setConstraints(chat, c);
    contentPane.add(chat);

    showMessage("Welcome to " + TITLE + "!");
    chat.showMessagePart("Enter ");
    chat.showActionMessagePart("/help", "/help");
    chat.showMessagePart(" for a list of available commands.\n");
    if (settingsHaveBeenLoaded)
      showMessage("Settings have been loaded from " + settingsFileName + ".");

    //
    // Create the menus:
      //
      JMenuBar menuBar = new JMenuBar();
      setJMenuBar(menuBar);
      JMenu gameMenu = new JMenu("Game");
      JMenu moveMenu = new JMenu("Move");
      JMenu settingsMenu = new JMenu("Settings");
      JMenu aboutMenu = new JMenu("About");
      menuBar.add(gameMenu);
      menuBar.add(moveMenu);
      menuBar.add(settingsMenu);
      menuBar.add(aboutMenu);

      JMenuItem menuItemNewWindow = new JMenuItem("New window");
      menuItemNewWindow.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          JChessBoard jcb = new JChessBoard(TITLE);
        }
      });
      gameMenu.add(menuItemNewWindow);

      menuItemNewGame = new JMenuItem("New game");
      menuItemNewGame.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            showMessage("Requesting new game...");
            protocol.requestNewGame();
          } else {
            newGame();
            showMessage("New game.");
          }
        }
      });
      gameMenu.add(menuItemNewGame);

      menuItemLoadFile = new JMenuItem("Load file...");
      menuItemLoadFile.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isFileChanged())
            askForSaving();
          JFileChooser fileChooser = new JFileChooser();
          fileChooser.setCurrentDirectory(new java.io.File(fileChooserDir));
          fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
          fileChooser.resetChoosableFileFilters();
          fileChooser.addChoosableFileFilter(new PGNFileFilter());
          int choice = fileChooser.showOpenDialog(null);
          if (choice == JFileChooser.APPROVE_OPTION) {
            loadFile(fileChooser.getSelectedFile());
            fileName = fileChooser.getSelectedFile().getName();
            setTitle(TITLE + " - " + fileName);
            fileIsChanged = false;
          }
        }
      });
      gameMenu.add(menuItemLoadFile);

      menuItemSave = new JMenuItem("Save");
      menuItemSave.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (fileName != null && fileName.length() > 0) {
            save(fileName, false);
          } else {
            saveAs();
          }
        }
      });
      gameMenu.add(menuItemSave);

      menuItemSaveAs = new JMenuItem("Save as...");
      menuItemSaveAs.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          saveAs();
        }
      });
      gameMenu.add(menuItemSaveAs);

      JMenuItem menuItemOpenBoardEditor = new JMenuItem("Open board editor...");
      menuItemOpenBoardEditor.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          new BoardEditor(JChessBoard.this);
        }
      });
      gameMenu.add(menuItemOpenBoardEditor);

      gameMenu.addSeparator();

      menuItemConnect = new JMenuItem("Connect...");
      menuItemConnect.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          Object message[] = new Object[4];
          message[0] = "Hostname: ";
          JTextField hostField = new JTextField(otherHostname, 30);
          message[1] = hostField;
          message[2] = "Port: ";
          JTextField portField = new JTextField("" + settings.networkPort, 5);
          message[3] = portField;
          Object options[] = { "OK", "Cancel" };
          int choice =
            JOptionPane.showOptionDialog(
                null,
                message,
                "Connect...",
                0,
                JOptionPane.QUESTION_MESSAGE,
                null,
                options,
                options[0]);
          if (choice == 0) {
            try {
              otherHostname = hostField.getText();
              int tmpPort = Integer.parseInt(portField.getText());
              if (tmpPort > 65535)
                throw (new NumberFormatException());
              settings.networkPort = tmpPort;
              boardConnector = new BoardConnector(JChessBoard.this, otherHostname, settings.networkPort);
              whitePlayer = UNKNOWN;
              blackPlayer = UNKNOWN;
              protocol.connecting();
            } catch (NumberFormatException e) {
              JOptionPane.showMessageDialog(
                  null,
                  "Illegal port: " + portField.getText(),
                  "Illegal port",
                  JOptionPane.ERROR_MESSAGE);
            }
          }
          updateMenu();
        }
      });
      gameMenu.add(menuItemConnect);

      menuItemCloseConnection = new JMenuItem("Close connection");
      menuItemCloseConnection.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          int choice =
            JOptionPane.showConfirmDialog(
                null,
                "Are you sure?",
                    "Close connection?",
                    JOptionPane.YES_NO_OPTION);
          if (choice == JOptionPane.YES_OPTION) {
            boardConnector.closeConnection();
            connectionClosed();
          }
        }
      });
      gameMenu.add(menuItemCloseConnection);
      menuItemCloseConnection.setEnabled(false);

      menuItemOfferDraw = new JMenuItem("Offer draw");
      menuItemOfferDraw.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            int choice =
              JOptionPane.showConfirmDialog(null, "Are you sure?", "Offer draw?", JOptionPane.YES_NO_OPTION);
            if (choice == JOptionPane.YES_OPTION) {
              showMessage("Offering draw...");
              protocol.offerDraw();
            }
          }
        }
      });
      gameMenu.add(menuItemOfferDraw);
      menuItemOfferDraw.setEnabled(false);

      menuItemResign = new JMenuItem("Resign");
      menuItemResign.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            int choice =
              JOptionPane.showConfirmDialog(null, "Are you sure?", "Resign?", JOptionPane.YES_NO_OPTION);
            if (choice == JOptionPane.YES_OPTION) {
              showMessage("Resigned.");
              //                        stopGame();
              protocol.resigned();
              if (whitePlayer == HUMAN)
                history.setResult("0-1");
              else if (blackPlayer == HUMAN)
                history.setResult("1-0");
            }
          }
        }
      });
      gameMenu.add(menuItemResign);
      menuItemResign.setEnabled(false);

      menuItemSwitchSides = new JMenuItem("Switch sides");
      menuItemSwitchSides.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            int choice =
              JOptionPane.showConfirmDialog(
                  null,
                  "Are you sure?",
                      "Switch sides?",
                          JOptionPane.YES_NO_OPTION);
            if (choice == JOptionPane.YES_OPTION) {
              showMessage("Requesting side switch...");
              protocol.requestSideSwitch();
            }
          }
        }
      });
      gameMenu.add(menuItemSwitchSides);

      menuItemUploadGame = new JMenuItem("Upload this game to peer");
      menuItemUploadGame.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            showMessage("Asking opponent...");
            protocol.requestUpload();
          }
        }
      });
      gameMenu.add(menuItemUploadGame);

      gameMenu.addSeparator();

      JMenuItem item2 = new JMenuItem("Exit");
      item2.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          exit();
        }
      });
      gameMenu.add(item2);

      undoMove = new JMenuItem("Take back last move");
      undoMove.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            showMessage("Requesting undo...");
            protocol.requestUndo();
          } else
            undoMove();
        }
      });
      moveMenu.add(undoMove);

      JMenuItem editComment = new JMenuItem("Edit comment");
      editComment.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          history.editComment();
          JChessBoard.this.update();
        }
      });
      moveMenu.add(editComment);

      clockCheckbox = new JCheckBoxMenuItem("Clock");
      clockCheckbox.setSelected(settings.enableClock);
      clockCheckbox.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          if (isConnected()) {
            clockCheckbox.setSelected(!clockCheckbox.isSelected());
            // Reverse what Swing has done until accepted by peer.
            showMessage("Asking opponent...");
            protocol.requestToggleClock();
          } else {
            setEnableClock(clockCheckbox.isSelected());
          }
        }
      });
      settingsMenu.add(clockCheckbox);

      JMenuItem gameTimeItem = new JMenuItem("Set time...");
      gameTimeItem.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          showTimeoutSelector();
        }
      });
      settingsMenu.add(gameTimeItem);

      settingsMenu.addSeparator();

      enableBeep = new JCheckBoxMenuItem("Beep");
      enableBeep.setSelected(settings.enableBeep);
      enableBeep.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          settings.enableBeep = enableBeep.isSelected();
        }
      });
      settingsMenu.add(enableBeep);

      reverseBoard = new JCheckBoxMenuItem("Reverse board");
      reverseBoard.setSelected(false);
      reverseBoard.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          visualBoard.setReverseBoard(reverseBoard.isSelected());
        }
      });
      settingsMenu.add(reverseBoard);

      showPossibleMoves = new JCheckBoxMenuItem("Show possible moves");
      showPossibleMoves.setSelected(settings.showPossibleMoves);
      showPossibleMoves.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          settings.showPossibleMoves = showPossibleMoves.isSelected();
        }
      });
      settingsMenu.add(showPossibleMoves);

      settingsMenu.addSeparator();

      final JMenuItem aiLevelEasy = new JRadioButtonMenuItem("AI level: Easy");
      final JMenuItem aiLevelMedium = new JRadioButtonMenuItem("AI level: Medium");
      final JMenuItem aiLevelBest = new JRadioButtonMenuItem("AI level: Best");

      aiLevelEasy.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          settings.aiLevel=AI.EASY;
          ai.setLevel(settings.aiLevel);
          aiLevelEasy.setSelected(true);
          aiLevelMedium.setSelected(false);
          aiLevelBest.setSelected(false);
        }
      });
      aiLevelMedium.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          settings.aiLevel=AI.MEDIUM;
          ai.setLevel(settings.aiLevel);
          aiLevelEasy.setSelected(false);
          aiLevelMedium.setSelected(true);
          aiLevelBest.setSelected(false);
        }
      });
      aiLevelBest.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          settings.aiLevel=AI.BEST;
          ai.setLevel(settings.aiLevel);
          aiLevelEasy.setSelected(false);
          aiLevelMedium.setSelected(false);
          aiLevelBest.setSelected(true);
        }
      });
      settingsMenu.add(aiLevelEasy);
      settingsMenu.add(aiLevelMedium);
      settingsMenu.add(aiLevelBest);

      aiLevelEasy.setSelected(settings.aiLevel==AI.EASY);
      aiLevelMedium.setSelected(settings.aiLevel==AI.MEDIUM);
      aiLevelBest.setSelected(settings.aiLevel==AI.BEST);
     
      settingsMenu.addSeparator();

      JMenuItem serverSettingsItem = new JMenuItem("Server settings...");
      serverSettingsItem.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          connectionListener.showSettingsPane();
          settings.enableServer = connectionListener.isEnabled();
          settings.networkPort = connectionListener.getPort();
        }
      });
      settingsMenu.add(serverSettingsItem);

      settingsMenu.addSeparator();

      JMenuItem saveSettingsItem = new JMenuItem("Save settings");
      saveSettingsItem.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          Dimension size = JChessBoard.this.getSize();
          settings.windowWidth = (int) size.getWidth();
          settings.windowHeight = (int) size.getHeight();
          try {
            settings.save();
            showMessage("Settings saved to " + settingsFileName + ".");
          } catch (IOException e) {
            showMessage("IOException saving settings: " + e);
          }
        }
      });
      settingsMenu.add(saveSettingsItem);

      JMenuItem licensingItem = new JMenuItem("Licensing");
      licensingItem.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
          JOptionPane.showMessageDialog(null, COPYRIGHT_MESSAGE, "About", JOptionPane.INFORMATION_MESSAGE);
        }
      });
      aboutMenu.add(licensingItem);

      JMenuItem versionsItem = new JMenuItem("Versions");
      versionsItem.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          JOptionPane.showMessageDialog(
              null,
              AI.getVersion()
              + "\n"
              + BoardConnector.getVersion()
              + "\n"
              + Chat.getVersion()
              + "\n"
              + ChessClock.getVersion()
              + "\n"
              + ConnectionListener.getVersion()
              + "\n"
              + ConnectionIndicator.getVersion()
              + "\n"
              + History.getVersion()
              + "\n"
              + JChessBoard.getVersion()
              + "\n"
              + Move.getVersion()
              + "\n"
              + PGN.getVersion()
              + "\n"
              + VirtualBoard.getVersion()
              + "\n"
              + VisualBoard.getVersion(),
              "Version Information",
              JOptionPane.INFORMATION_MESSAGE);
        }
      });
      aboutMenu.add(versionsItem);

      updateMenu();
      updateNavigationButtons();
      pack();
      setSize(settings.windowWidth, settings.windowHeight);
      //        setResizable(false);
      setVisible(true);

      addComponentListener(new ComponentListener() {
        public void componentResized(ComponentEvent e) {
          int width = getWidth();
          int height = getHeight();
          if (width < MIN_WIDTH || height < MIN_HEIGHT) {
            width = (width < MIN_WIDTH) ? MIN_WIDTH : width;
            height = (height < MIN_HEIGHT) ? MIN_HEIGHT : height;
            JChessBoard.this.setSize(width, height);
          }
        }
        public void componentMoved(ComponentEvent e) {
        }
        public void componentShown(ComponentEvent e) {
        }
        public void componentHidden(ComponentEvent e) {
        }
      });

      addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          exit();
        }
      });
      fileIsChanged = false;
      newGame();
      ai=new AI(JChessBoard.this);
      ai.setLevel(settings.aiLevel);
  }

  // main
  public static void main(final String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        JChessBoard jcb = new JChessBoard(TITLE);
        if (args.length > 0) {
          jcb.loadFile(new java.io.File(args[0]));
        }
      }
    });
  }
}
TOP

Related Classes of jchessboard.JChessBoard$PGNFileFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.