package dev.kske.chess.ui; import java.awt.Desktop; import java.awt.Toolkit; import java.awt.dnd.DropTarget; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; import javax.swing.*; 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: Chess
* File: MainWindow.java
* Created: 01.07.2019
* * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class MainWindow extends JFrame { private JTabbedPane tabbedPane = new JTabbedPane(); private static final long serialVersionUID = -3100939302567978977L; /** * Launch the application. * * @param args command line arguments are ignored */ public static void main(String[] args) { SwingUtilities.invokeLater(MainWindow::new); } /** * Create the application. */ private MainWindow() { super("Chess by Kai S. K. Engelbart"); // Configure frame setResizable(false); setBounds(100, 100, 494, 565); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setIconImage( Toolkit.getDefaultToolkit() .getImage(getClass().getResource("/pieces/queen_white.png")) ); // Add tabbed pane, menu bar and drop target getContentPane().add(tabbedPane); setJMenuBar(new MenuBar(this)); new DropTarget(this, new GameDropTarget(this)); // Update position and dimensions pack(); setLocationRelativeTo(null); setVisible(true); } /** * @return the currently selected {@link GamePane} component */ public GamePane getSelectedGamePane() { return (GamePane) tabbedPane.getSelectedComponent(); } /** * Creates a new {@link GamePane}, adds it to the tabbed pane and opens it. * The new tab has the title {@code Game n} where {@code n} is its number. * * @return the new {@link GamePane} */ public GamePane addGamePane() { return addGamePane("Game " + (tabbedPane.getTabCount() + 1)); } /** * Creates a new {@link GamePane}, adds it to the tabbed pane and opens it. * * @param title The title of the {@link GamePane} * @return the new {@link GamePane} */ public GamePane addGamePane(String title) { GamePane gamePane = new GamePane(); tabbedPane.add(title, gamePane); tabbedPane.setTabComponentAt( tabbedPane.getTabCount() - 1, new GameTabComponent(tabbedPane) ); tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1); return gamePane; } /** * Creates a new {@link GamePane}, adds it to the tabbed pane and * immediately * 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} */ 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. * * @param index The index of the {@link GamePane} to remove */ public void removeGamePane(int 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 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 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!" ); 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 ); } }); } /** * 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} */ public void saveFile(File file) { 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, "Failed to save the file " + file.getName() + ": " + e, "File saving error", JOptionPane.ERROR_MESSAGE ); } } }