2019-07-06 16:37:42 +02:00
|
|
|
package dev.kske.chess.ui;
|
2019-07-01 18:26:43 +02:00
|
|
|
|
2019-11-24 21:31:32 +01:00
|
|
|
import java.awt.Desktop;
|
2019-07-18 11:19:58 +02:00
|
|
|
import java.awt.Toolkit;
|
2019-08-24 16:04:09 +02:00
|
|
|
import java.awt.dnd.DropTarget;
|
2019-10-25 16:58:18 +02:00
|
|
|
import java.io.File;
|
2019-11-08 15:22:12 +01:00
|
|
|
import java.io.IOException;
|
2019-10-25 16:58:18 +02:00
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import java.nio.file.Files;
|
|
|
|
import java.util.List;
|
2019-07-01 18:26:43 +02:00
|
|
|
|
2020-01-19 22:12:33 +01:00
|
|
|
import javax.swing.*;
|
2019-07-01 18:26:43 +02:00
|
|
|
|
2019-10-25 16:58:18 +02:00
|
|
|
import dev.kske.chess.board.Board;
|
|
|
|
import dev.kske.chess.board.FENString;
|
|
|
|
import dev.kske.chess.exception.ChessException;
|
|
|
|
import dev.kske.chess.game.Game;
|
|
|
|
import dev.kske.chess.pgn.PGNDatabase;
|
|
|
|
import dev.kske.chess.pgn.PGNGame;
|
|
|
|
|
2019-07-01 18:26:43 +02:00
|
|
|
/**
|
|
|
|
* Project: <strong>Chess</strong><br>
|
2019-07-06 16:37:42 +02:00
|
|
|
* File: <strong>MainWindow.java</strong><br>
|
2019-07-01 18:26:43 +02:00
|
|
|
* Created: <strong>01.07.2019</strong><br>
|
2019-12-11 14:02:31 +01:00
|
|
|
*
|
2019-10-26 07:55:21 +02:00
|
|
|
* @since Chess v0.1-alpha
|
|
|
|
* @author Kai S. K. Engelbart
|
2019-07-01 18:26:43 +02:00
|
|
|
*/
|
2019-09-08 20:18:45 +02:00
|
|
|
public class MainWindow extends JFrame {
|
2019-07-01 18:26:43 +02:00
|
|
|
|
2020-02-23 21:21:19 +01:00
|
|
|
private JTabbedPane tabbedPane = new JTabbedPane();
|
2019-09-08 20:18:45 +02:00
|
|
|
|
2020-02-23 21:21:19 +01:00
|
|
|
private static final long serialVersionUID = -3100939302567978977L;
|
2019-07-01 18:26:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Launch the application.
|
2020-01-19 22:12:33 +01:00
|
|
|
*
|
|
|
|
* @param args command line arguments are ignored
|
2019-07-01 18:26:43 +02:00
|
|
|
*/
|
2020-05-19 16:50:12 +02:00
|
|
|
public static void main(String[] args) {
|
|
|
|
SwingUtilities.invokeLater(MainWindow::new);
|
|
|
|
}
|
2019-07-01 18:26:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create the application.
|
|
|
|
*/
|
2020-02-23 21:21:19 +01:00
|
|
|
private MainWindow() {
|
2019-09-08 20:18:45 +02:00
|
|
|
super("Chess by Kai S. K. Engelbart");
|
2019-07-01 18:26:43 +02:00
|
|
|
|
2019-08-23 22:00:30 +02:00
|
|
|
// Configure frame
|
2019-09-08 20:18:45 +02:00
|
|
|
setResizable(false);
|
|
|
|
setBounds(100, 100, 494, 565);
|
2020-05-19 16:50:12 +02:00
|
|
|
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
|
|
|
setIconImage(
|
|
|
|
Toolkit.getDefaultToolkit()
|
|
|
|
.getImage(getClass().getResource("/pieces/queen_white.png"))
|
|
|
|
);
|
2019-08-23 22:00:30 +02:00
|
|
|
|
2020-02-23 21:21:19 +01:00
|
|
|
// Add tabbed pane, menu bar and drop target
|
2019-09-08 20:18:45 +02:00
|
|
|
getContentPane().add(tabbedPane);
|
|
|
|
setJMenuBar(new MenuBar(this));
|
2019-10-25 16:58:18 +02:00
|
|
|
new DropTarget(this, new GameDropTarget(this));
|
2019-08-23 22:00:30 +02:00
|
|
|
|
|
|
|
// Update position and dimensions
|
2019-09-08 20:18:45 +02:00
|
|
|
pack();
|
|
|
|
setLocationRelativeTo(null);
|
|
|
|
setVisible(true);
|
2019-07-01 18:26:43 +02:00
|
|
|
}
|
2019-07-14 12:03:45 +02:00
|
|
|
|
2019-08-23 22:00:30 +02:00
|
|
|
/**
|
2020-01-19 22:12:33 +01:00
|
|
|
* @return the currently selected {@link GamePane} component
|
2019-08-23 22:00:30 +02:00
|
|
|
*/
|
2020-05-19 16:50:12 +02:00
|
|
|
public GamePane getSelectedGamePane() {
|
|
|
|
return (GamePane) tabbedPane.getSelectedComponent();
|
|
|
|
}
|
2019-09-01 12:45:06 +02:00
|
|
|
|
|
|
|
/**
|
2019-09-01 18:57:31 +02:00
|
|
|
* Creates a new {@link GamePane}, adds it to the tabbed pane and opens it.
|
2019-10-18 17:45:13 +02:00
|
|
|
* The new tab has the title {@code Game n} where {@code n} is its number.
|
2019-12-11 14:02:31 +01:00
|
|
|
*
|
2020-01-19 22:12:33 +01:00
|
|
|
* @return the new {@link GamePane}
|
2019-09-01 12:45:06 +02:00
|
|
|
*/
|
2020-05-19 16:50:12 +02:00
|
|
|
public GamePane addGamePane() {
|
|
|
|
return addGamePane("Game " + (tabbedPane.getTabCount() + 1));
|
|
|
|
}
|
2019-10-18 17:45:13 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new {@link GamePane}, adds it to the tabbed pane and opens it.
|
2019-12-11 14:02:31 +01:00
|
|
|
*
|
2019-10-18 17:45:13 +02:00
|
|
|
* @param title The title of the {@link GamePane}
|
2020-01-19 22:12:33 +01:00
|
|
|
* @return the new {@link GamePane}
|
2019-10-18 17:45:13 +02:00
|
|
|
*/
|
|
|
|
public GamePane addGamePane(String title) {
|
2019-09-01 12:45:06 +02:00
|
|
|
GamePane gamePane = new GamePane();
|
2019-10-18 17:45:13 +02:00
|
|
|
tabbedPane.add(title, gamePane);
|
2020-05-19 16:50:12 +02:00
|
|
|
tabbedPane.setTabComponentAt(
|
|
|
|
tabbedPane.getTabCount() - 1,
|
|
|
|
new GameTabComponent(tabbedPane)
|
|
|
|
);
|
2019-12-11 14:02:31 +01:00
|
|
|
tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
|
2019-09-01 12:45:06 +02:00
|
|
|
return gamePane;
|
|
|
|
}
|
2019-09-08 20:18:45 +02:00
|
|
|
|
2020-01-19 22:12:33 +01:00
|
|
|
/**
|
2020-05-19 16:50:12 +02:00
|
|
|
* Creates a new {@link GamePane}, adds it to the tabbed pane and
|
|
|
|
* immediately
|
2020-01-19 22:12:33 +01:00
|
|
|
* displays a game configuration dialog for a new game on an existing
|
|
|
|
* {@link Board}.
|
|
|
|
*
|
|
|
|
* @param title the title of the {@link GamePane}
|
|
|
|
* @param board the {@link Board} with which the new {@link Game} is started
|
|
|
|
* @return the new {@link GamePane}
|
|
|
|
*/
|
2019-10-25 16:58:18 +02:00
|
|
|
public GamePane addGamePane(String title, Board board) {
|
|
|
|
GamePane gamePane = addGamePane(title);
|
2020-05-19 16:50:12 +02:00
|
|
|
DialogUtil.showGameConfigurationDialog(
|
|
|
|
this,
|
|
|
|
(whiteName, blackName) -> {
|
|
|
|
Game game = new Game(
|
|
|
|
gamePane.getBoardPane(),
|
|
|
|
whiteName,
|
|
|
|
blackName,
|
|
|
|
board
|
|
|
|
);
|
|
|
|
gamePane.setGame(game);
|
|
|
|
game.start();
|
|
|
|
}
|
|
|
|
);
|
2019-10-25 16:58:18 +02:00
|
|
|
return gamePane;
|
|
|
|
}
|
|
|
|
|
2019-09-08 20:18:45 +02:00
|
|
|
/**
|
|
|
|
* Removes a {@link GamePane} form the tabbed pane.
|
2019-12-11 14:02:31 +01:00
|
|
|
*
|
2019-09-08 20:18:45 +02:00
|
|
|
* @param index The index of the {@link GamePane} to remove
|
|
|
|
*/
|
2020-05-19 16:50:12 +02:00
|
|
|
public void removeGamePane(int index) {
|
|
|
|
tabbedPane.remove(index);
|
|
|
|
}
|
2019-10-25 16:58:18 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads a game file (FEN or PGN) and adds it to a new {@link GamePane}.
|
2019-12-11 14:02:31 +01:00
|
|
|
*
|
2019-10-25 16:58:18 +02:00
|
|
|
* @param files the files to load the game from
|
|
|
|
*/
|
|
|
|
public void loadFiles(List<File> files) {
|
|
|
|
files.forEach(file -> {
|
2020-05-19 16:50:12 +02:00
|
|
|
final String name
|
|
|
|
= file.getName().substring(0, file.getName().lastIndexOf('.'));
|
|
|
|
final String extension = file.getName()
|
|
|
|
.substring(file.getName().lastIndexOf('.'))
|
|
|
|
.toLowerCase();
|
2019-10-25 16:58:18 +02:00
|
|
|
try {
|
|
|
|
Board board;
|
|
|
|
switch (extension) {
|
|
|
|
case ".fen":
|
2020-05-19 16:50:12 +02:00
|
|
|
board = new FENString(
|
|
|
|
new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)
|
|
|
|
).getBoard();
|
2019-10-25 16:58:18 +02:00
|
|
|
break;
|
|
|
|
case ".pgn":
|
|
|
|
PGNDatabase pgnDB = new PGNDatabase();
|
|
|
|
pgnDB.load(file);
|
|
|
|
if (pgnDB.getGames().size() > 0) {
|
2020-05-19 16:50:12 +02:00
|
|
|
String[] gameNames
|
|
|
|
= new String[pgnDB.getGames().size()];
|
2019-10-25 16:58:18 +02:00
|
|
|
for (int i = 0; i < gameNames.length; i++) {
|
|
|
|
final PGNGame game = pgnDB.getGames().get(i);
|
2020-05-19 16:50:12 +02:00
|
|
|
gameNames[i] = String.format(
|
|
|
|
"%s vs %s: %s",
|
|
|
|
game.getTag("White"),
|
|
|
|
game.getTag("Black"),
|
|
|
|
game.getTag("Result")
|
|
|
|
);
|
2019-10-25 16:58:18 +02:00
|
|
|
}
|
2020-05-19 16:50:12 +02:00
|
|
|
JComboBox<String> comboBox
|
|
|
|
= new JComboBox<>(gameNames);
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
this,
|
|
|
|
comboBox,
|
|
|
|
"Select a game",
|
|
|
|
JOptionPane.QUESTION_MESSAGE
|
|
|
|
);
|
|
|
|
board = pgnDB.getGames()
|
|
|
|
.get(comboBox.getSelectedIndex())
|
|
|
|
.getBoard();
|
|
|
|
} else
|
|
|
|
throw new ChessException(
|
|
|
|
"The PGN database '" + name + "' is empty!"
|
|
|
|
);
|
2019-10-25 16:58:18 +02:00
|
|
|
break;
|
|
|
|
default:
|
2020-05-19 16:50:12 +02:00
|
|
|
throw new ChessException(
|
|
|
|
"The file extension '" + extension
|
|
|
|
+ "' is not supported!"
|
|
|
|
);
|
2019-10-25 16:58:18 +02:00
|
|
|
}
|
|
|
|
addGamePane(name, board);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2020-05-19 16:50:12 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
this,
|
|
|
|
"Failed to load the file " + file.getName() + ": "
|
|
|
|
+ e.toString(),
|
|
|
|
"File loading error",
|
|
|
|
JOptionPane.ERROR_MESSAGE
|
|
|
|
);
|
2019-10-25 16:58:18 +02:00
|
|
|
}
|
|
|
|
});
|
2019-09-08 20:18:45 +02:00
|
|
|
}
|
2019-11-08 15:22:12 +01:00
|
|
|
|
2020-01-19 22:12:33 +01:00
|
|
|
/**
|
|
|
|
* Saves the current {@link Game} as a file in {@code PGN} or {@code FEN}
|
|
|
|
* format.
|
|
|
|
*
|
|
|
|
* @param file the file in which to save the current {@link Game}
|
|
|
|
*/
|
2019-11-08 15:22:12 +01:00
|
|
|
public void saveFile(File file) {
|
2020-05-19 16:50:12 +02:00
|
|
|
final int dotIndex = file.getName().lastIndexOf('.');
|
|
|
|
final String extension
|
|
|
|
= file.getName().substring(dotIndex).toLowerCase();
|
|
|
|
|
|
|
|
if (extension.equals(".pgn"))
|
|
|
|
try {
|
|
|
|
PGNGame pgnGame
|
|
|
|
= new PGNGame(getSelectedGamePane().getGame().getBoard());
|
|
|
|
pgnGame.setTag(
|
|
|
|
"Event",
|
|
|
|
tabbedPane.getTitleAt(tabbedPane.getSelectedIndex())
|
|
|
|
);
|
|
|
|
pgnGame.setTag("Result", "*");
|
|
|
|
PGNDatabase pgnDB = new PGNDatabase();
|
|
|
|
pgnDB.getGames().add(pgnGame);
|
|
|
|
pgnDB.save(file);
|
|
|
|
|
|
|
|
if (
|
|
|
|
JOptionPane.showConfirmDialog(
|
|
|
|
this,
|
|
|
|
"Game export finished. Do you want to view the created file?",
|
|
|
|
"Game export finished",
|
|
|
|
JOptionPane.YES_NO_OPTION
|
|
|
|
) == JOptionPane.YES_OPTION
|
|
|
|
)
|
|
|
|
Desktop.getDesktop().open(file);
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
this,
|
2020-05-16 22:43:17 +02:00
|
|
|
"Failed to save the file " + file.getName() + ": " + e,
|
2019-11-08 15:22:12 +01:00
|
|
|
"File saving error",
|
2020-05-19 16:50:12 +02:00
|
|
|
JOptionPane.ERROR_MESSAGE
|
|
|
|
);
|
|
|
|
}
|
2019-11-08 15:22:12 +01:00
|
|
|
}
|
2019-07-01 18:26:43 +02:00
|
|
|
}
|