Refined IO functionality, fixed FEN string serialization and deserialization #7
@ -15,6 +15,7 @@ import javax.swing.JFileChooser;
|
|||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.filechooser.FileFilter;
|
||||||
|
|
||||||
import dev.kske.chess.io.EngineUtil;
|
import dev.kske.chess.io.EngineUtil;
|
||||||
|
|
||||||
@ -28,11 +29,25 @@ public class DialogUtil {
|
|||||||
|
|
||||||
private DialogUtil() {}
|
private DialogUtil() {}
|
||||||
|
|
||||||
public static void showFileSelectionDialog(Component parent, Consumer<File> action) {
|
public static void showFileSelectionDialog(Component parent, Consumer<List<File>> action) {
|
||||||
JFileChooser fileChooser = new JFileChooser();
|
JFileChooser fileChooser = new JFileChooser();
|
||||||
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
|
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
|
||||||
if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION)
|
fileChooser.setAcceptAllFileFilterUsed(false);
|
||||||
action.accept(fileChooser.getSelectedFile());
|
fileChooser.addChoosableFileFilter(new FileFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File f) {
|
||||||
|
int dotIndex = f.getName().lastIndexOf('.');
|
||||||
|
if (dotIndex >= 0) {
|
||||||
|
String extension = f.getName().substring(dotIndex).toLowerCase();
|
||||||
|
return extension.equals(".fen") || extension.equals(".pgn");
|
||||||
|
} else return f.isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() { return "FEN and PGN files"; }
|
||||||
|
});
|
||||||
|
if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) action.accept(Arrays.asList(fileChooser.getSelectedFile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showGameConfigurationDialog(Component parent, BiConsumer<String, String> action) {
|
public static void showGameConfigurationDialog(Component parent, BiConsumer<String, String> action) {
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
package dev.kske.chess.ui;
|
|
||||||
|
|
||||||
import java.awt.datatransfer.DataFlavor;
|
|
||||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
|
||||||
import java.awt.dnd.DnDConstants;
|
|
||||||
import java.awt.dnd.DropTargetAdapter;
|
|
||||||
import java.awt.dnd.DropTargetDropEvent;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
|
|
||||||
import dev.kske.chess.board.FENString;
|
|
||||||
import dev.kske.chess.exception.ChessException;
|
|
||||||
import dev.kske.chess.game.Game;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Project: <strong>Chess</strong><br>
|
|
||||||
* File: <strong>FENDropTarget.java</strong><br>
|
|
||||||
* Created: <strong>13 Aug 2019</strong><br>
|
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
|
||||||
*/
|
|
||||||
public class FENDropTarget extends DropTargetAdapter {
|
|
||||||
|
|
||||||
private MainWindow mainWindow;
|
|
||||||
|
|
||||||
public FENDropTarget(MainWindow mainWindow) { this.mainWindow = mainWindow; }
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public void drop(DropTargetDropEvent evt) {
|
|
||||||
try {
|
|
||||||
evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
|
|
||||||
((List<File>) evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor)).forEach(file -> {
|
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
|
||||||
final GamePane gamePane = mainWindow.addGamePane();
|
|
||||||
final String fen = br.readLine();
|
|
||||||
DialogUtil.showGameConfigurationDialog(null, (whiteName, blackName) -> {
|
|
||||||
Game game;
|
|
||||||
try {
|
|
||||||
game = new Game(gamePane.getBoardPane(), whiteName, blackName, new FENString(fen).getBoard());
|
|
||||||
gamePane.setGame(game);
|
|
||||||
game.start();
|
|
||||||
} catch (ChessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
|
||||||
"Failed to load FEN string: " + e.toString(),
|
|
||||||
"FEN loading error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
evt.dropComplete(true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
evt.rejectDrop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (UnsupportedFlavorException | IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
evt.rejectDrop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
35
src/dev/kske/chess/ui/GameDropTarget.java
Normal file
35
src/dev/kske/chess/ui/GameDropTarget.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package dev.kske.chess.ui;
|
||||||
|
|
||||||
|
import java.awt.datatransfer.DataFlavor;
|
||||||
|
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||||
|
import java.awt.dnd.DnDConstants;
|
||||||
|
import java.awt.dnd.DropTargetAdapter;
|
||||||
|
import java.awt.dnd.DropTargetDropEvent;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Chess</strong><br>
|
||||||
|
* File: <strong>GameDropTarget.java</strong><br>
|
||||||
|
* Created: <strong>13 Aug 2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class GameDropTarget extends DropTargetAdapter {
|
||||||
|
|
||||||
|
private MainWindow mainWindow;
|
||||||
|
|
||||||
|
public GameDropTarget(MainWindow mainWindow) { this.mainWindow = mainWindow; }
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void drop(DropTargetDropEvent evt) {
|
||||||
|
try {
|
||||||
|
evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
|
||||||
|
mainWindow.loadFiles((List<File>) evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
|
||||||
|
} catch (UnsupportedFlavorException | IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
evt.rejectDrop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,23 @@ package dev.kske.chess.ui;
|
|||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.dnd.DropTarget;
|
import java.awt.dnd.DropTarget;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
* File: <strong>MainWindow.java</strong><br>
|
* File: <strong>MainWindow.java</strong><br>
|
||||||
@ -55,7 +68,7 @@ public class MainWindow extends JFrame {
|
|||||||
getContentPane().add(tabbedPane);
|
getContentPane().add(tabbedPane);
|
||||||
|
|
||||||
setJMenuBar(new MenuBar(this));
|
setJMenuBar(new MenuBar(this));
|
||||||
new DropTarget(this, new FENDropTarget(this));
|
new DropTarget(this, new GameDropTarget(this));
|
||||||
|
|
||||||
// Update position and dimensions
|
// Update position and dimensions
|
||||||
pack();
|
pack();
|
||||||
@ -74,9 +87,7 @@ public class MainWindow extends JFrame {
|
|||||||
*
|
*
|
||||||
* @return The new {@link GamePane}
|
* @return The new {@link GamePane}
|
||||||
*/
|
*/
|
||||||
public GamePane addGamePane() {
|
public GamePane addGamePane() { return addGamePane("Game " + (tabbedPane.getComponentCount() + 1)); }
|
||||||
return addGamePane("Game " + (tabbedPane.getComponentCount() + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link GamePane}, adds it to the tabbed pane and opens it.
|
* Creates a new {@link GamePane}, adds it to the tabbed pane and opens it.
|
||||||
@ -91,12 +102,64 @@ public class MainWindow extends JFrame {
|
|||||||
return gamePane;
|
return gamePane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GamePane addGamePane(String title, Board board) {
|
||||||
|
GamePane gamePane = addGamePane(title);
|
||||||
|
DialogUtil.showGameConfigurationDialog(this,
|
||||||
|
(whiteName, blackName) -> {
|
||||||
|
Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, board);
|
||||||
|
gamePane.setGame(game);
|
||||||
|
game.start();
|
||||||
|
});
|
||||||
|
return gamePane;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a {@link GamePane} form the tabbed pane.
|
* Removes a {@link GamePane} form the tabbed pane.
|
||||||
*
|
*
|
||||||
* @param index The index of the {@link GamePane} to remove
|
* @param index The index of the {@link GamePane} to remove
|
||||||
*/
|
*/
|
||||||
public void removeGamePane(int index) {
|
public void removeGamePane(int index) { tabbedPane.remove(index); }
|
||||||
tabbedPane.remove(index);
|
|
||||||
|
/**
|
||||||
|
* Loads a game file (FEN or PGN) and adds it to a new {@link GamePane}.
|
||||||
|
*
|
||||||
|
* @param files the files to load the game from
|
||||||
|
*/
|
||||||
|
public void loadFiles(List<File> files) {
|
||||||
|
files.forEach(file -> {
|
||||||
|
final String name = file.getName().substring(0, file.getName().lastIndexOf('.'));
|
||||||
|
final String extension = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase();
|
||||||
|
try {
|
||||||
|
Board board;
|
||||||
|
switch (extension) {
|
||||||
|
case ".fen":
|
||||||
|
board = new FENString(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)).getBoard();
|
||||||
|
break;
|
||||||
|
case ".pgn":
|
||||||
|
PGNDatabase pgnDB = new PGNDatabase();
|
||||||
|
pgnDB.load(file);
|
||||||
|
if (pgnDB.getGames().size() > 0) {
|
||||||
|
String[] gameNames = new String[pgnDB.getGames().size()];
|
||||||
|
for (int i = 0; i < gameNames.length; i++) {
|
||||||
|
final PGNGame game = pgnDB.getGames().get(i);
|
||||||
|
gameNames[i] = String.format("%s vs %s: %s", game.getTag("White"), game.getTag("Black"), game.getTag("Result"));
|
||||||
|
}
|
||||||
|
JComboBox<String> comboBox = new JComboBox<>(gameNames);
|
||||||
|
JOptionPane.showInputDialog(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!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ChessException("The file extension '" + extension + "' is not supported!");
|
||||||
|
}
|
||||||
|
addGamePane(name, board);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
JOptionPane.showMessageDialog(this,
|
||||||
|
"Failed to load the file " + file.getName() + ": " + e.toString(),
|
||||||
|
"File loading error",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,7 @@ package dev.kske.chess.ui;
|
|||||||
|
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.io.File;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
@ -16,8 +12,6 @@ import dev.kske.chess.board.FENString;
|
|||||||
import dev.kske.chess.exception.ChessException;
|
import dev.kske.chess.exception.ChessException;
|
||||||
import dev.kske.chess.game.Game;
|
import dev.kske.chess.game.Game;
|
||||||
import dev.kske.chess.io.EngineUtil;
|
import dev.kske.chess.io.EngineUtil;
|
||||||
import dev.kske.chess.pgn.PGNDatabase;
|
|
||||||
import dev.kske.chess.pgn.PGNGame;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -52,7 +46,7 @@ public class MenuBar extends JMenuBar {
|
|||||||
gameMenu.add(newGameMenuItem);
|
gameMenu.add(newGameMenuItem);
|
||||||
|
|
||||||
JMenuItem loadFileMenu = new JMenuItem("Load game file");
|
JMenuItem loadFileMenu = new JMenuItem("Load game file");
|
||||||
loadFileMenu.addActionListener((evt) -> DialogUtil.showFileSelectionDialog(mainWindow, this::loadFile));
|
loadFileMenu.addActionListener((evt) -> DialogUtil.showFileSelectionDialog(mainWindow, mainWindow::loadFiles));
|
||||||
gameMenu.add(loadFileMenu);
|
gameMenu.add(loadFileMenu);
|
||||||
|
|
||||||
add(gameMenu);
|
add(gameMenu);
|
||||||
@ -108,73 +102,4 @@ public class MenuBar extends JMenuBar {
|
|||||||
|
|
||||||
add(toolsMenu);
|
add(toolsMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFile(File file) {
|
|
||||||
final String name = file.getName().substring(0, file.getName().lastIndexOf('.'));
|
|
||||||
final String extension = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase();
|
|
||||||
switch (extension) {
|
|
||||||
case ".fen":
|
|
||||||
try {
|
|
||||||
final GamePane gamePane = mainWindow.addGamePane(name);
|
|
||||||
final String fen = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
|
|
||||||
DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
|
|
||||||
Game game;
|
|
||||||
try {
|
|
||||||
game = new Game(gamePane.getBoardPane(), whiteName, blackName, new FENString(fen).getBoard());
|
|
||||||
gamePane.setGame(game);
|
|
||||||
game.start();
|
|
||||||
} catch (ChessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
|
||||||
"Failed to load FEN string: " + e.toString(),
|
|
||||||
"FEN loading error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
|
||||||
"Failed to load the file " + file.getName() + ": " + e.toString(),
|
|
||||||
"File loading error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ".pgn":
|
|
||||||
try {
|
|
||||||
final GamePane gamePane = mainWindow.addGamePane(name);
|
|
||||||
PGNDatabase pgnDB = new PGNDatabase();
|
|
||||||
pgnDB.load(file);
|
|
||||||
if (pgnDB.getGames().size() > 0) {
|
|
||||||
String[] gameNames = new String[pgnDB.getGames().size()];
|
|
||||||
for (int i = 0; i < gameNames.length; i++) {
|
|
||||||
final PGNGame game = pgnDB.getGames().get(i);
|
|
||||||
gameNames[i] = String.format("%s vs %s: %s", game.getTag("White"), game.getTag("Black"), game.getTag("Result"));
|
|
||||||
}
|
|
||||||
JComboBox<String> comboBox = new JComboBox<>(gameNames);
|
|
||||||
JOptionPane.showInputDialog(mainWindow, comboBox, "Select a game", JOptionPane.QUESTION_MESSAGE);
|
|
||||||
DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
|
|
||||||
final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName,
|
|
||||||
pgnDB.getGames().get(comboBox.getSelectedIndex()).getBoard());
|
|
||||||
game.start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
|
||||||
"Failed to load the file " + file.getName() + ": " + e.toString(),
|
|
||||||
"File loading error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
|
||||||
"The file extension '" + extension + "' is not supported!",
|
|
||||||
"File loading error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFENFile(File file) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user