package com.kodcu.boot;
import com.install4j.api.launcher.StartupNotification;
import com.kodcu.config.ConfigurationService;
import com.kodcu.config.EditorConfigBean;
import com.kodcu.controller.ApplicationController;
import com.kodcu.service.ThreadService;
import com.kodcu.service.ui.TabService;
import de.tototec.cmdoption.CmdlineParser;
import de.tototec.cmdoption.CmdlineParserException;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Paths;
import static javafx.scene.input.KeyCombination.SHORTCUT_DOWN;
public class AppStarter extends Application {
private static Logger logger = LoggerFactory.getLogger(AppStarter.class);
private static ApplicationController controller;
private static ConfigurableApplicationContext context;
private EditorConfigBean editorConfigBean;
private Stage stage;
private ThreadService threadService;
private ConfigurationService configurationService;
@Override
public void start(final Stage stage) {
// System.setProperty("nashorn.typeInfo.maxFiles", "5");
// http://bit.ly/1Euk8hh
System.setProperty("jsse.enableSNIExtension", "false");
// System.setProperty("https.protocols", "SSLv3");
final CmdlineConfig config = new CmdlineConfig();
final CmdlineParser cp = new CmdlineParser(config);
try {
cp.parse(getParameters().getRaw().toArray(new String[0]));
} catch (final CmdlineParserException e) {
System.err.println("Invalid commandline given: " + e.getMessage());
System.exit(1);
}
if (config.help) {
cp.usage();
System.exit(0);
}
new Thread(() -> {
try {
startApp(stage, config);
} catch (final Throwable e) {
logger.error("Problem occured while starting AsciidocFX", e);
}
}).start();
}
private void startApp(final Stage stage, final CmdlineConfig config) throws Throwable {
this.stage = stage;
context = SpringApplication.run(SpringAppConfig.class);
editorConfigBean = context.getBean(EditorConfigBean.class);
controller = context.getBean(ApplicationController.class);
threadService = context.getBean(ThreadService.class);
configurationService = context.getBean(ConfigurationService.class);
final FXMLLoader parentLoader = new FXMLLoader();
parentLoader.setControllerFactory(context::getBean);
InputStream sceneStream = getClass().getResourceAsStream("/fxml/Scene.fxml");
Parent root = parentLoader.load(sceneStream);
Scene scene = new Scene(root);
stage.setTitle("AsciidocFX");
InputStream logoStream = getClass().getResourceAsStream("/logo.png");
stage.getIcons().add(new Image(logoStream));
threadService.runActionLater(stage::setScene, scene);
controller.initializeApp();
stage.setOnShowing(e -> {
controller.setStage(stage);
controller.setScene(scene);
controller.setHostServices(getHostServices());
configurationService.loadConfigurations();
controller.applyInitialConfigurations();
});
stage.setOnShown(e -> {
controller.bindConfigurations();
});
threadService.runActionLater(() -> {
setMaximized();
if (controller.getTabPane().getTabs().isEmpty()) {
controller.newDoc();
}
stage.show();
});
IOUtils.closeQuietly(sceneStream);
IOUtils.closeQuietly(logoStream);
final FXMLLoader asciidocTableLoader = new FXMLLoader();
final FXMLLoader markdownTableLoader = new FXMLLoader();
asciidocTableLoader.setControllerFactory(context::getBean);
markdownTableLoader.setControllerFactory(context::getBean);
InputStream asciidocTableStream = getClass().getResourceAsStream("/fxml/AsciidocTablePopup.fxml");
AnchorPane asciidocTableAnchor = asciidocTableLoader.load(asciidocTableStream);
InputStream markdownTableStream = getClass().getResourceAsStream("/fxml/MarkdownTablePopup.fxml");
AnchorPane markdownTableAnchor = markdownTableLoader.load(markdownTableStream);
Stage asciidocTableStage = threadService.supply(Stage::new);
threadService.runActionLater(asciidocTableStage::setScene, new Scene(asciidocTableAnchor));
asciidocTableStage.setTitle("Table Generator");
asciidocTableStage.initModality(Modality.WINDOW_MODAL);
asciidocTableStage.initOwner(scene.getWindow());
asciidocTableStage.getIcons().add(new Image(logoStream));
Stage markdownTableStage = threadService.supply(Stage::new);
threadService.runActionLater(markdownTableStage::setScene, new Scene(markdownTableAnchor));
markdownTableStage.setTitle("Table Generator");
markdownTableStage.initModality(Modality.WINDOW_MODAL);
markdownTableStage.initOwner(scene.getWindow());
markdownTableStage.getIcons().add(new Image(logoStream));
IOUtils.closeQuietly(asciidocTableStream);
IOUtils.closeQuietly(markdownTableStream);
controller.setAsciidocTableAnchor(asciidocTableAnchor);
controller.setMarkdownTableAnchor(markdownTableAnchor);
controller.setAsciidocTableStage(asciidocTableStage);
controller.setAsciidocTableScene(asciidocTableStage.getScene());
controller.setMarkdownTableStage(markdownTableStage);
controller.setMarkdownTableScene(markdownTableStage.getScene());
controller.applyCurrentTheme(asciidocTableStage.getScene(), markdownTableStage.getScene());
controller.initializeSaveOnBlur();
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.S, SHORTCUT_DOWN), controller::saveDoc);
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.M, SHORTCUT_DOWN), controller::adjustSplitPane);
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.N, SHORTCUT_DOWN), controller::newDoc);
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.O, SHORTCUT_DOWN), controller::openDoc);
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.W, SHORTCUT_DOWN), controller::saveAndCloseCurrentTab);
final ThreadService threadService = context.getBean(ThreadService.class);
controller.initializeTabWatchListener();
threadService.start(() -> {
try {
registerStartupListener(config);
} catch (Exception e) {
logger.error("Problem occured in startup listener", e);
}
});
scene.getWindow().setOnCloseRequest(controller::closeAllTabs);
stage.widthProperty().addListener(controller::stageWidthChanged);
stage.heightProperty().addListener(controller::stageWidthChanged);
}
private void setMaximized() {
Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
stage.setX(bounds.getMinX());
stage.setY(bounds.getMinY());
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
}
private void registerStartupListener(CmdlineConfig config) {
final ThreadService threadService = context.getBean(ThreadService.class);
final TabService tabService = context.getBean(TabService.class);
StartupNotification.registerStartupListener(parameters -> {
threadService.runActionLater(() -> {
String[] files = parameters.split(" ");
for (String file : files) {
file = file.replace("\"", "");
tabService.addTab(Paths.get(file).toAbsolutePath());
}
});
});
if (!config.files.isEmpty()) {
threadService.runActionLater(() -> {
config.files.stream().forEach(f -> {
File file = new File(f).getAbsoluteFile();
if (file.exists()) {
logger.info("Opening file as requsted from cmdline: {}", file);
tabService.addTab(file.toPath());
} else {
// TODO: do we want to create such a file on demand?
logger.error("Cannot open non-existent file: {}", file);
}
});
});
}
}
@Override
public void stop() throws Exception {
controller.closeApp(null);
context.registerShutdownHook();
Platform.exit();
System.exit(0);
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}