ca.wumbo.doommanager.client.controller.CoreController.java Source code

Java tutorial

Introduction

Here is the source code for ca.wumbo.doommanager.client.controller.CoreController.java

Source

/*
 * 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;
    }
}