Java tutorial
/* * DoomManager * Copyright (C) 2014 Chris K * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package ca.wumbo.doommanager.client.controller; import java.io.IOException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import ca.wumbo.doommanager.client.Client; import ca.wumbo.doommanager.client.util.JavaFXTicker; import ca.wumbo.doommanager.core.config.ClientConfig; import ca.wumbo.doommanager.core.config.Config; import ca.wumbo.doommanager.net.NetworkSelector; import ca.wumbo.doommanager.util.Tickable; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Label; import javafx.scene.control.TabPane; import javafx.scene.control.Tab; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import javafx.stage.WindowEvent; /** * The main controller class that houses all the tabs and the top level GUI. */ public class CoreController implements Tickable { /** * The logger for this class. */ private static final Logger log = LogManager.getLogger(CoreController.class); /** * How many nanoseconds are in a millisecond. */ private static final long MS_PER_NANO = 1000000; /** * The ticking rate of the main application. This is 15 ms currently. */ private static final long TICRATE_NANO = 15 * MS_PER_NANO; /** * The main stage (should be the primary stage passed from the Core * (Application) start method. */ private Stage stage; /** * The main scene for the stage. */ private Scene scene; /** * A ticker that pulses every so often to run repetitive tasks. One useful * example of this is periodically checking network selectors. */ private JavaFXTicker ticker; /** * The last nanoseconds whereby this module has been ticked. */ private long lastTickNano; @Autowired private Config config; @Autowired private StartController startController; @Autowired private FileEditorController fileEditorController; @Autowired private MapEditorController mapEditorController; @Autowired private TextureDatabaseController textureDatabaseController; @Autowired private ServerBrowserController serverBrowserController; @Autowired private IRCController ircController; @Autowired private OptionsController optionsController; @Autowired private ConsoleController consoleController; @Autowired private NetworkSelector clientSelector; @Value("${core.controller.fxmlpath}") private String fxmlPath; @Value("${css.default.path}") private String cssPath; @Value("${doommanager.title}") private String applicationTitle; @Value("${doommanager.version}") private String applicationVersion; //========================================================================= @FXML private BorderPane rootBorderPane; //------------------------------------------------------------------------- // Center pane //------------------------------------------------------------------------- @FXML private TabPane tabPane; @FXML private Tab mainTab; @FXML private Tab fileEditorTab; @FXML private Tab mapEditorTab; @FXML private Tab textureDatabaseTab; @FXML private Tab serverBrowserTab; @FXML private Tab ircTab; @FXML private Tab optionsTab; @FXML private Tab consoleTab; //------------------------------------------------------------------------- // Bottom pane //------------------------------------------------------------------------- @FXML private AnchorPane bottomAnchorPane; @FXML private Label statusLabel; @FXML private Rectangle statusBackgroundShape; //========================================================================= /** * Only to be instantiated by Spring. */ private CoreController() { lastTickNano = 0; } /** * The starting point of the application. The Core should pass the first * stage (what should be the primary stage) to this method, which will in * turn cascade and create all the * * @param primaryStage * The stage created by the main JavaFX thread at the beginning. * * @throws IOException * If there is an error loading the FXML file. * * @throws NullPointerException * If the argument is null. */ public void start(Stage primaryStage) throws IOException { if (primaryStage == null) throw new NullPointerException("Passed a null stage to start."); // Load the required data into ourselves. FXMLLoader loader = new FXMLLoader(Client.class.getResource(fxmlPath)); loader.setControllerFactory((cls) -> { return this; }); loader.load(); // Add the loaded tab panes into their respective tabs. mainTab.setContent(startController.getRootPane()); fileEditorTab.setContent(fileEditorController.getRootPane()); mapEditorTab.setContent(mapEditorController.getRootPane()); textureDatabaseTab.setContent(textureDatabaseController.getRootPane()); serverBrowserTab.setContent(serverBrowserController.getRootPane()); ircTab.setContent(ircController.getRootPane()); optionsTab.setContent(optionsController.getRootPane()); consoleTab.setContent(consoleController.getRootPane()); // Set up the scene and stage and display it. ClientConfig clientConfig = config.getClientConfig(); Scene primaryScene = new Scene(rootBorderPane, clientConfig.getStartingWidth(), clientConfig.getStartingHeight()); scene = primaryScene; scene.getStylesheets().add(cssPath); stage = primaryStage; stage.setTitle(applicationTitle + " v" + applicationVersion); stage.setMaximized(clientConfig.isStartMaximized()); stage.setScene(scene); stage.show(); // Start the ticker now that everything is ready to go. ticker = new JavaFXTicker((int) (TICRATE_NANO / MS_PER_NANO), event -> tick()); ticker.start(); // Set up the network connections by starting the selector. if (clientSelector.errorOccured()) { Alert alert = new Alert(Alert.AlertType.WARNING, "Unable to request network channels from OS."); // TODO - make less cryptic. alert.show(); } else if (!clientSelector.selectorIsOpen()) { Alert alert = new Alert(Alert.AlertType.WARNING, "Unable to open network channels."); // TODO - make less cryptic. alert.show(); } // Set up the stage exit handling. stage.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, event -> ticker.stop()); log.info("Welcome to " + applicationTitle + " v" + applicationVersion + "!"); } @FXML public void initialize() { // Need to stretch the shape rectangle to fill the entire bottom. statusBackgroundShape.widthProperty().bind(bottomAnchorPane.widthProperty()); statusBackgroundShape.heightProperty().bind(bottomAnchorPane.heightProperty()); } @Override public void tick() { // Don't tick if we've done it recently. if (System.nanoTime() - lastTickNano < TICRATE_NANO) return; // Anything we do while ticking should count to our total time, therefore mark it first. lastTickNano = System.nanoTime(); // Tick the network handler first so the module ticking uses up-to-date data. // Note: It will return safely if there is an underlying error, so we don't need to check. clientSelector.tick(); // Tick the underlying modules. fileEditorController.tick(); mapEditorController.tick(); textureDatabaseController.tick(); serverBrowserController.tick(); ircController.tick(); } /** * Attempts to terminate the application. This will request the main window * to close, and the closer listener will take care of any logic needed * before shutting down (like asking the user if that's what they want, and * to save anything... etc). */ public void exit() { stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST)); } /** * Gets the main Stage window for the application. * * @return * The primary stage. */ public Stage getStage() { return stage; } }