package mj.ocraptor.javafx; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import javafx.application.Application; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXMLLoader; import javafx.fxml.JavaFXBuilderFactory; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.WindowEvent; import mj.ocraptor.MainController; import mj.ocraptor.configuration.Config; import mj.ocraptor.configuration.Localization; import mj.ocraptor.configuration.properties.ConfigString; import mj.ocraptor.database.search.LuceneResult; import mj.ocraptor.events.EventManager; import mj.ocraptor.events.ProgressUpdate; import mj.ocraptor.events.Queue; import mj.ocraptor.events.QueueMonitor; import mj.ocraptor.javafx.controllers.Confirmation; import mj.ocraptor.javafx.controllers.HelpBrowser; import mj.ocraptor.javafx.controllers.LoadingScreen; import mj.ocraptor.javafx.controllers.MessageDialog; import mj.ocraptor.javafx.controllers.SearchDialog; import mj.ocraptor.javafx.controllers.SelectDatabase; /** * * * @author */ public class GUIController extends Application { private Stage primaryStage; private Stage secondaryStage; private Stage thirdStage; private Stage forthStage; private MainController mainController; private Config cfg; private MessageDialog messageDialog; private GUITemplate template; private Queue<ProgressUpdate> messageQueue; private Queue<String> exceptionQueue; private Image mainIcon; private static GUIController instance; private LuceneResult searchResult; private String contentSearchLuceneQuery; private String lastContentSearch; private boolean showMoreDetails; private Thread exceptionWorker; private Localization localization; private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger( GUIController.class); /** * * * @param configuration */ public boolean loadConfiguration(String configuration) throws Exception { final File configFile = new File(configuration); if (configFile.exists() && configFile.canWrite()) { final File dbFolder = new File(this.cfg.getProp(configuration, ConfigString.DATABASE_FOLDER)); if (dbFolder.exists() && dbFolder.canWrite()) { this.cfg.setConfigUserFilePath(configuration); this.cfg.updateFileProperties(); this.mainController.initMultiCoreProcessing(); this.mainController.refresh(); return true; } } return false; } // JavaFX Application Thread @Override public void start(Stage primaryStage) throws Exception { try { Thread.currentThread().setName(Config.APP_NAME + "JavaFX: started thread"); GUIController.instance = this; final String iconPath = this.getClass().getResource(Icon.STAGE_ICON.getFileName()).toString(); this.mainIcon = new Image(iconPath); this.mainController = MainController.inst(); this.primaryStage = primaryStage; this.cfg = Config.inst(); this.localization = Localization.instance(); EventManager.instance().addGUIHandler(); EventManager.instance().initLoggerAppender(); String lastCheckPoint = this.cfg.getProp(this.cfg.getConfigMasterFilePath(), ConfigString.LAST_SESSION_CHECKPOINT); String lastUsedConfiguration = this.cfg.getProp(this.cfg.getConfigMasterFilePath(), ConfigString.LAST_USED_CONFIGURATION); boolean restoreSession = false; if (!lastUsedConfiguration.isEmpty()) { String path = this.cfg.getUserFolder() + File.separator + lastUsedConfiguration + Config.PROPERTIES_EXTENSION; try { restoreSession = this.loadConfiguration(path); } catch (Exception e) { e.printStackTrace(); } } // TODO: restore session ? if (restoreSession && lastCheckPoint.equals(SearchDialog.FXML)) { this.gotoPage(SearchDialog.FXML, SearchDialog.INIT_WIDTH, SearchDialog.INIT_HEIGHT); this.mainController.initDatabase(); this.cfg.setProp(this.cfg.getConfigMasterFilePath(), ConfigString.LAST_SESSION_CHECKPOINT, ""); } else { this.gotoPage(SelectDatabase.FXML, SelectDatabase.INIT_WIDTH, SelectDatabase.INIT_HEIGHT); } // this.gotoPage(SettingsManager.FXML, SettingsManager.INIT_WIDTH, // SettingsManager.INIT_HEIGHT); // this.gotoPage(SearchDialog.FXML, SearchDialog.INIT_WIDTH, // SearchDialog.INIT_HEIGHT); this.primaryStage.getIcons().add(mainIcon); primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { public void handle(WindowEvent we) { Platform.runLater(new Runnable() { @Override public void run() { Thread.currentThread().setName(Config.APP_NAME + "JavaFX: exit application"); exitApplication(); } }); } }); // primaryStage.initStyle(StageStyle.UTILITY); // primaryStage.initStyle(StageStyle.UNDECORATED); this.exceptionQueue = new QueueMonitor<String>(10); if (exceptionWorker == null) { exceptionWorker = new Thread(exceptionWorker()); exceptionWorker.setDaemon(true); exceptionWorker.start(); } } catch (Exception ex) { // TODO: ex.printStackTrace(); } } /** * * * @param results * @param contentSearch */ public void setSearchResult(final LuceneResult results, final String contentSearch) { this.searchResult = results; this.contentSearchLuceneQuery = contentSearch; this.showMoreDetails = false; } /** * * */ public void exitApplication() { if (this.mainController != null) { this.mainController.shutdown(true); } if (this.primaryStage != null) { this.primaryStage.close(); this.primaryStage = null; } if (this.secondaryStage != null) { this.secondaryStage.close(); this.secondaryStage = null; } if (this.thirdStage != null) { this.thirdStage.close(); this.thirdStage = null; } if (this.forthStage != null) { this.forthStage.close(); this.forthStage = null; } } /** * * */ public void pauseIndexing() { if (this.template != null && this.template instanceof LoadingScreen) { ((LoadingScreen) this.template).pauseProcess(); } } /** * * * @param question */ public void showConfirmationDialog(String question) { this.showConfirmationDialog(question, null, 350, 100, false); } /** * * * @param question */ public void showConfirmationDialog(String question, double initWidth, double initHeight) { this.showConfirmationDialog(question, null, initWidth, initHeight, false); } /** * * * @param question * @param handler * @param initWidth * @param initHeight */ public void showConfirmationDialog(String question, EventHandler<ActionEvent> handler, double initWidth, double initHeight) { this.showConfirmationDialog(question, handler, initWidth, initHeight, true); } /** * * * @param question * @param handler */ public void showConfirmationDialog(String question, EventHandler<ActionEvent> handler) { this.showConfirmationDialog(question, handler, 350, 100, true); } /** * * * @param template */ public void showConfirmationDialog(String question, EventHandler<ActionEvent> handler, double initWidth, double initHeight, boolean showCancelButton) { try { Confirmation.setINIT_WIDTH(initWidth); Confirmation.setINIT_HEIGHT(initHeight); Confirmation page = (Confirmation) showAsPopupWindow(FXMLFile.CONFIRMATION.toString(), false, false, true, initWidth, initHeight); page.addExternalOKButtonHandler(handler); page.addExternalCancelButtonHandler(null); page.setConfirmationText(question); if (!showCancelButton) { page.removeCancelButton(); } page.initUserComponents(); } catch (Exception ex) { // TODO: ex.printStackTrace(); } } /** * * * @param question * @param yesHandler * @param noHandler * @param addCancelButton */ public void showYesNoDialog(String question, EventHandler<ActionEvent> yesHandler, EventHandler<ActionEvent> noHandler, boolean addCancelButton) { showYesNoDialog(question, yesHandler, noHandler, addCancelButton, 350, 100); } /** * * * @param template */ public void showYesNoDialog(String question, EventHandler<ActionEvent> yesHandler, EventHandler<ActionEvent> noHandler, boolean addCancelButton, double width, double height) { try { Confirmation.setINIT_WIDTH(width); Confirmation.setINIT_HEIGHT(height); Confirmation page = (Confirmation) showAsPopupWindow(FXMLFile.CONFIRMATION.toString(), false, true, true, width, height); page.addExternalOKButtonHandler(yesHandler); page.addExternalNOButtonHandler(noHandler); if (addCancelButton) { page.addExternalCancelButtonHandler(null); } page.setConfirmationText(question); page.initUserComponents(); } catch (Exception ex) { // TODO: ex.printStackTrace(); } } /** * * * @param message */ public void showSimpleMessage(String message) { Text blackText = new Text(message); showMessage(300, 400, 5, "...", Color.BLACK, true, blackText); } /** * * * @param template */ public void showMessage(double width, double height, double rightPadding, String title, Color titleColor, boolean wrapText, Node... messages) { try { MessageDialog.setINIT_WIDTH(width); MessageDialog.setINIT_HEIGHT(height); MessageDialog page = (MessageDialog) showAsPopupWindow(FXMLFile.MESSAGE_DIALOG.toString(), false, false, false, MessageDialog.INIT_WIDTH, MessageDialog.INIT_HEIGHT); if (wrapText) { page.wrapText(); } page.setRightPadding(rightPadding); page.addNodes(messages); page.setTitle(title, titleColor); page.initUserComponents(); } catch (Exception ex) { // TODO: ex.printStackTrace(); } } // ------------------------------------------------ // // -- // ------------------------------------------------ // private double lastPageWidth = 0; private double lastPageHeight = 0; /** * * * @param template */ public void gotoPage(final String template, final Double width, final Double height) { try { this.template = (GUITemplate) replaceSceneContent(template, width, height); this.template.initUserComponents(); } catch (Exception ex) { ex.printStackTrace(); } } /** * * * @param fxml * @return * * @throws Exception */ private Node replaceSceneContent(final String fxml, final Double width, final Double height) throws Exception { FXMLLoader loader = new FXMLLoader(); InputStream in = SelectDatabase.class.getResourceAsStream(fxml); loader.setBuilderFactory(new JavaFXBuilderFactory()); loader.setLocation(SelectDatabase.class.getResource(fxml)); AnchorPane page = null; try { page = (AnchorPane) loader.load(in); } catch (Exception e) { // TODO: e.printStackTrace(); } finally { if (in != null) { in.close(); } } // TODO: provokes // this.primaryStage.setMinWidth(width - 50); // this.primaryStage.setMinHeight(height - 30); page.setPrefSize(width, height); // store the stage width and height in case the user has resized the window double stageWidth = primaryStage.getWidth(); double stageHeight = primaryStage.getHeight(); try { if (!Double.isNaN(stageWidth)) { stageWidth -= (primaryStage.getWidth() - primaryStage.getScene().getWidth()); } if (!Double.isNaN(stageHeight)) { stageHeight -= (primaryStage.getHeight() - primaryStage.getScene().getHeight()); } } catch (Exception e) { } double currentPageWidth = page.getPrefWidth(); double currentPageHeight = page.getPrefHeight(); Scene scene = new Scene(page); primaryStage.setScene(scene); // TODO: split screen: boolean thresholdReached = (Math.abs(stageWidth - this.lastPageWidth) > 100 && Math.abs( stageHeight - this.lastPageHeight) > 200) || primaryStage.isMaximized() || primaryStage .isFullScreen(); // boolean thresholdReached = primaryStage.isMaximized() || // primaryStage.isFullScreen(); if (!Double.isNaN(stageWidth) && thresholdReached) { page.setPrefWidth(stageWidth); } if (!Double.isNaN(stageHeight) && thresholdReached) { page.setPrefHeight(stageHeight); } primaryStage.setTitle(getText("TEMPLATE.TITLE_TEXT", Config.APP_NAME, Config.HARDCODED_VERSION)); this.lastPageWidth = currentPageWidth; this.lastPageHeight = currentPageHeight; if (!primaryStage.isShowing()) { primaryStage.show(); primaryStage.centerOnScreen(); } primaryStage.sizeToScene(); // javafx.geometry.Rectangle2D primScreenBounds = // Screen.getPrimary().getVisualBounds(); // primaryStage.setX((primScreenBounds.getWidth() - primaryStage.getWidth()) // / 2); // primaryStage.setY((primScreenBounds.getHeight() - // primaryStage.getHeight()) / 2); Platform.runLater(new Runnable() { @Override public void run() { Thread.currentThread().setName(Config.APP_NAME + "Closing SplashScreen"); mainController.closeSplashScreen(); } }); // this.primaryStage.show(); return (Node) loader.getController(); } /** * * * @param fxml * @param exceptionStage */ protected void showHelp(String url, String anchor) { try { HelpBrowser page = (HelpBrowser) showAsPopupWindow(FXMLFile.HELP_BROWSER.toString(), false, false, false, HelpBrowser.INIT_WIDTH, HelpBrowser.INIT_HEIGHT); page.initUserComponents(); page.setURL(url, anchor); } catch (Exception e) { // TODO: logging e.printStackTrace(); } } /** * * * @param fxml */ private Node showAsPopupWindow(String fxml, boolean exceptionStage, boolean setOwner, boolean forceUserToInteract, Double width, Double height) { AnchorPane root; try { Stage stage = new Stage(); stage.setWidth(width); stage.setHeight(height); stage.setMinWidth(150); stage.setMinHeight(100); stage.getIcons().add(this.mainIcon); stage.setTitle(Config.APP_NAME); stage.setX(primaryStage.getX() + primaryStage.getWidth() / 2 - stage.getWidth() / 2); stage.setY(primaryStage.getY() + primaryStage.getHeight() / 2 - stage.getHeight() / 2); if (forceUserToInteract) { stage.initModality(Modality.APPLICATION_MODAL); } else if (setOwner) { stage.initOwner(primaryStage); } FXMLLoader loader = new FXMLLoader(); InputStream in = SelectDatabase.class.getResourceAsStream(fxml); loader.setBuilderFactory(new JavaFXBuilderFactory()); loader.setLocation(SelectDatabase.class.getResource(fxml)); try { root = (AnchorPane) loader.load(in); } finally { if (in != null) { in.close(); } } root.setPrefSize(width, height); Node node = (Node) loader.getController(); // ------------------------------------------------ // // -- // ------------------------------------------------ // if (node != null) { if (node instanceof HelpBrowser) { if (this.forthStage != null) { this.forthStage.close(); } this.forthStage = stage; } else if (node instanceof MessageDialog) { if (!exceptionStage) { if (this.secondaryStage != null) { this.secondaryStage.close(); } this.secondaryStage = stage; } else { this.thirdStage = stage; } } else if (node instanceof Confirmation) { if (this.secondaryStage != null) { this.secondaryStage.close(); } this.secondaryStage = stage; } } stage.setScene(new Scene(root)); stage.sizeToScene(); stage.show(); Platform.runLater(new Runnable() { @Override public void run() { stage.toFront(); } }); // ------------------------------------------------ // return node; } catch (IOException e) { e.printStackTrace(); } return null; } // ------------------------------------------------ // // -- // ------------------------------------------------ // /** * * * @param property * @param placeHolders * @return */ public String getText(final String property, final Object... placeHolders) { return localization.getText(property, placeHolders); } // ------------------------------------------------ // // -- exception handling // ------------------------------------------------ // /** * * * @param template */ public void showExceptionScreen(String message) { try { if (message != null && !message.trim().isEmpty()) { final double width = 800; final double height = 500; MessageDialog.setINIT_WIDTH(width); MessageDialog.setINIT_HEIGHT(height); Text redText = null; boolean firstMessage = false; if (this.messageDialog == null) { this.messageDialog = (MessageDialog) showAsPopupWindow(FXMLFile.MESSAGE_DIALOG.toString(), true, false, false, width, height); firstMessage = true; Platform.runLater(new Runnable() { @Override public void run() { primaryStage.toFront(); } }); } else { firstMessage = false; } redText = new Text(message); redText.setFill(Color.BLACK); redText.setFont(Font.font(java.awt.Font.MONOSPACED, 13)); if (this.messageDialog != null) { messageDialog.initUserComponents(); messageDialog.addNodes(true, false, firstMessage, redText); messageDialog.enableCloseApplicationButton(); messageDialog.enableHelpButton(); messageDialog.setTitle(getText("ERROR.ERROR_OCCURRED"), Color.GREEN); } } } catch (Exception ex) { // TODO: ex.printStackTrace(); } } private static final int REFRESH_TIME_EXCEPTION_IN_MS = 1000; private static final int FIRST_EXCEPTION_WAITTIME = 100; private long startTimeMessageDialog = System.currentTimeMillis(); /** * * * @return */ private Task<Object> exceptionWorker() { return new Task<Object>() { @Override protected Object call() throws Exception { Thread.currentThread().setName(Config.APP_NAME + "JavaFX: show exception screen - 1"); startTimeMessageDialog = System.currentTimeMillis(); while (!Thread.currentThread().isInterrupted()) { String message = exceptionQueue.get(); if (messageDialog == null || secondaryStage == null) { Thread.sleep(FIRST_EXCEPTION_WAITTIME); } long timePassed = System.currentTimeMillis() - startTimeMessageDialog; if (timePassed < REFRESH_TIME_EXCEPTION_IN_MS) { Thread.sleep(REFRESH_TIME_EXCEPTION_IN_MS - timePassed); } startTimeMessageDialog = System.currentTimeMillis(); Platform.runLater(new Runnable() { @Override public void run() { Thread.currentThread().setName(Config.APP_NAME + "JavaFX: show exception screen - 2"); showExceptionScreen(message); } }); } return null; } }; } // ------------------------------------------------ // // -- // ------------------------------------------------ // /** * @return the stage */ public Stage getPrimaryStage() { return primaryStage; } /** * @return the ocraptor */ public MainController getParentController() { return mainController; } /** * @return the instance */ public static GUIController instance() { return instance; } /** * @return the template */ public GUITemplate getTemplate() { return template; } /** * @return the messageQueue */ public Queue<ProgressUpdate> getMessageQueue() { return messageQueue; } /** * @param messageQueue * the messageQueue to set */ public void setMessageQueue(Queue<ProgressUpdate> messageQueue) { this.messageQueue = messageQueue; } /** * @return the exceptionQueue */ public Queue<String> getExceptionQueue() { return exceptionQueue; } /** * @return the searchResult */ public LuceneResult getSearchResult() { return searchResult; } /** * @return the contentSearch */ public String getContentSearchLuceneQuery() { return contentSearchLuceneQuery; } /** * @return the showMoreDetails */ public boolean showMoreDetails() { return showMoreDetails; } /** * @return the lastContentSearch */ public String getLastContentSearch() { return lastContentSearch; } /** * @param lastContentSearch * the lastContentSearch to set */ public void setLastContentSearch(String lastContentSearch) { this.lastContentSearch = lastContentSearch; } /** * @param showMoreDetails * the showMoreDetails to set */ public void setShowMoreDetails(boolean showMoreDetails) { this.showMoreDetails = showMoreDetails; } }