net.rptools.tokentool.client.TokenTool.java Source code

Java tutorial

Introduction

Here is the source code for net.rptools.tokentool.client.TokenTool.java

Source

/*
 * This software Copyright by the RPTools.net development team, and licensed under the Affero GPL Version 3 or, at your option, any later version.
 *
 * TokenTool Source Code 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.
 *
 * You should have received a copy of the GNU Affero General Public License * along with this source Code. If not, please visit <http://www.gnu.org/licenses/> and specifically the Affero license text
 * at <http://www.gnu.org/licenses/agpl.html>.
 */
package net.rptools.tokentool.client;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.ResourceBundle;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.appender.FileAppender;

import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.application.Preloader;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import net.rptools.tokentool.AppConstants;
import net.rptools.tokentool.AppPreferences;
import net.rptools.tokentool.AppSetup;
import net.rptools.tokentool.controller.TokenTool_Controller;
import net.rptools.tokentool.util.I18N;
import net.rptools.tokentool.util.ImageUtil;

/**
 * 
 * @author Jamz
 * 
 *         To see splashscreen during testing, use JVM arg: -Djavafx.preloader=net.rptools.tokentool.client.SplashScreenLoader Otherwise splashscreen will only show when defined as
 *         JavaFX-Preloader-Class in the JAR manifest.
 * 
 */
public class TokenTool extends Application {
    private static TokenTool appInstance;

    private static Logger log; // Don't instantiate until AppSetup gets and sets user_home/logs directory in AppSetup

    private static BorderPane root;
    private static TokenTool_Controller tokentool_Controller;

    private static String VERSION = "";
    private static String VENDOR = "";

    private static final int THUMB_SIZE = 100;

    private static int overlayCount = 0;
    private static int loadCount = 1;
    private static double deltaX = 0;
    private static double deltaY = 0;

    private static TreeItem<Path> overlayTreeItems;
    private static Stage stage;

    @Override
    public void init() throws Exception {
        appInstance = this;
        VERSION = getVersion();

        // Lets install/update the overlays if newer version
        AppSetup.install(VERSION);
        log = LogManager.getLogger(TokenTool.class);
        log.info("3D Hardware Available? " + Platform.isSupported(ConditionalFeature.SCENE3D));

        // Now lets cache any overlays we find and update preLoader with progress
        overlayCount = (int) Files.walk(AppConstants.OVERLAY_DIR.toPath()).filter(Files::isRegularFile).count();
        overlayTreeItems = cacheOverlays(AppConstants.OVERLAY_DIR, null, THUMB_SIZE);

        // All Done!
        notifyPreloader(new Preloader.ProgressNotification(1.0));
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        stage = primaryStage;
        setUserAgentStylesheet(STYLESHEET_MODENA); // Setting the style back to the new Modena
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(AppConstants.TOKEN_TOOL_FXML),
                ResourceBundle.getBundle(AppConstants.TOKEN_TOOL_BUNDLE));
        root = fxmlLoader.load();
        tokentool_Controller = (TokenTool_Controller) fxmlLoader.getController();

        Scene scene = new Scene(root);
        primaryStage.setTitle(I18N.getString("TokenTool.stage.title"));
        primaryStage.getIcons().add(new Image(getClass().getResourceAsStream(AppConstants.TOKEN_TOOL_ICON)));
        primaryStage.setScene(scene);

        primaryStage.widthProperty().addListener((obs, oldVal, newVal) -> {
            if (Double.isNaN(oldVal.doubleValue()))
                return;

            deltaX += newVal.doubleValue() - oldVal.doubleValue();

            // Only adjust on even width adjustments
            if (deltaX > 1 || deltaX < -1) {
                if (deltaX % 2 == 0) {
                    tokentool_Controller.updatePortraitLocation(deltaX, 0);
                    deltaX = 0;
                } else {
                    tokentool_Controller.updatePortraitLocation(deltaX - 1, 0);
                    deltaX = 1;
                }
            }
        });

        primaryStage.heightProperty().addListener((obs, oldVal, newVal) -> {
            if (Double.isNaN(oldVal.doubleValue()))
                return;

            deltaY += newVal.doubleValue() - oldVal.doubleValue();

            // Only adjust on even width adjustments
            if (deltaY > 1 || deltaY < -1) {
                if (deltaY % 2 == 0) {
                    tokentool_Controller.updatePortraitLocation(0, deltaY);
                    deltaY = 0;
                } else {
                    tokentool_Controller.updatePortraitLocation(0, deltaY - 1);
                    deltaY = 1;
                }
            }
        });

        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                tokentool_Controller.exitApplication();
            }
        });

        // Load all the overlays into the treeview
        tokentool_Controller.updateOverlayTreeview(overlayTreeItems);

        // Restore saved settings
        AppPreferences.restorePreferences(tokentool_Controller);

        // Add recent list to treeview
        tokentool_Controller.updateOverlayTreeViewRecentFolder(true);

        // Set the Overlay Options accordion to be default open view
        tokentool_Controller.expandOverlayOptionsPane(true);

        primaryStage.show();

        // Finally, update token preview image after everything is done loading
        Platform.runLater(() -> tokentool_Controller.updateTokenPreviewImageView());
    }

    public static TokenTool getInstance() {
        return appInstance;
    }

    public Stage getStage() {
        return stage;
    }

    /**
     * 
     * @author Jamz
     * @throws IOException
     * @since 2.0
     * 
     *        This method loads and processes all the overlays found in user.home/overlays and it can take a minute to load as it creates thumbnail versions for the comboBox so we call this during the
     *        init and display progress in the preLoader (splash screen).
     * 
     */
    private TreeItem<Path> cacheOverlays(File dir, TreeItem<Path> parent, int THUMB_SIZE) throws IOException {
        TreeItem<Path> root = new TreeItem<>(dir.toPath());
        root.setExpanded(false);

        log.debug("caching " + dir.getAbsolutePath());

        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                cacheOverlays(file, root, THUMB_SIZE);
            } else {
                Path filePath = file.toPath();
                TreeItem<Path> imageNode = new TreeItem<>(filePath,
                        ImageUtil.getOverlayThumb(new ImageView(), filePath));
                root.getChildren().add(imageNode);

                notifyPreloader(new Preloader.ProgressNotification((double) loadCount++ / overlayCount));
            }
        }

        if (parent != null) {
            // When we show the overlay image, the TreeItem value is "" so we need to
            // sort those to the bottom for a cleaner look and keep sub dir's at the top.
            // If a node has no children then it's an overlay, otherwise it's a directory...
            root.getChildren().sort(new Comparator<TreeItem<Path>>() {
                @Override
                public int compare(TreeItem<Path> o1, TreeItem<Path> o2) {
                    if (o1.getChildren().size() == 0 && o2.getChildren().size() == 0)
                        return 0;
                    else if (o1.getChildren().size() == 0)
                        return Integer.MAX_VALUE;
                    else if (o2.getChildren().size() == 0)
                        return Integer.MIN_VALUE;
                    else
                        return o1.getValue().compareTo(o2.getValue());
                }
            });

            parent.getChildren().add(root);
        }

        return root;
    }

    public static String getVersion() {
        if (!VERSION.isEmpty())
            return VERSION;

        VERSION = "DEVELOPMENT";

        if (TokenTool.class.getPackage().getImplementationVersion() != null) {
            VERSION = TokenTool.class.getPackage().getImplementationVersion().trim();
        }

        return VERSION;
    }

    public static String getVendor() {
        if (!VENDOR.isEmpty())
            return VENDOR;

        if (TokenTool.class.getPackage().getImplementationVendor() != null) {
            VENDOR = TokenTool.class.getPackage().getImplementationVendor().trim();
        }

        return VENDOR;
    }

    private static String getCommandLineStringOption(Options options, String searchValue, String[] args) {
        CommandLineParser parser = new DefaultParser();

        try {
            CommandLine cmd = parser.parse(options, args);

            if (cmd.hasOption(searchValue)) {
                return cmd.getOptionValue(searchValue);
            }
        } catch (ParseException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        return "";
    }

    public static String getLoggerFileName() {
        org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) log;
        Appender appender = loggerImpl.getAppenders().get("LogFile");

        if (appender != null)
            return ((FileAppender) appender).getFileName();
        else
            return "NOT_CONFIGURED";
    }

    /**
     * Legacy, it simply launches the FX Application which calls init() then start()
     * 
     * @author Jamz
     * @since 2.0
     * 
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) {
        Options cmdOptions = new Options();
        cmdOptions.addOption("v", "version", true, "override version number"); //$NON-NLS-2$ //$NON-NLS-3$
        cmdOptions.addOption("n", "vendor", true, "override vendor"); //$NON-NLS-2$ //$NON-NLS-3$

        VERSION = getCommandLineStringOption(cmdOptions, "version", args);
        VENDOR = getCommandLineStringOption(cmdOptions, "vendor", args);

        launch(args);
    }
}