All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ensemble.Ensemble2 Maven / Gradle / Ivy

/*
 * Copyright (c) 2008, 2012 Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the distribution.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package ensemble;

import ensemble.config.ProxyDialog;
import ensemble.controls.BreadcrumbBar;
import ensemble.controls.SearchBox;
import ensemble.controls.WindowButtons;
import ensemble.controls.WindowResizeButton;
import ensemble.pages.SamplePage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
import javafx.animation.*;
import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.DepthTest;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import netscape.javascript.JSObject;

/**
 * Ensemble Main Application
 */
public class Ensemble2 extends Application {
    static {
        // Enable using system proxy if set
        try {
        System.setProperty("java.net.useSystemProxies", "true");
        } catch (SecurityException e) {
            // Don - Ignore this error - running in sandbox
        }
    }
    public static final String DEFAULT_DOCS_URL = "http://download.oracle.com/javafx/2/api/";
    
    private static Ensemble2 ensemble2;
    private static boolean isApplet = false;
    private Stage stage;
    private Scene scene;
    private BorderPane root;
    private ToolBar toolBar;
    private SplitPane splitPane;
    private TreeView pageTree;
    private Pane pageArea;
    private Pages pages;
    private Page currentPage;
    private String currentPagePath;
    private Node currentPageView;
    private BreadcrumbBar breadcrumbBar;
    private Stack history = new Stack();
    private Stack forwardHistory = new Stack();
    private boolean changingPage = false;
    private double mouseDragOffsetX = 0;
    private double mouseDragOffsetY = 0;
    private WindowResizeButton windowResizeButton;
    public boolean fromForwardOrBackButton = false;
    private StackPane modalDimmer;
    private ProxyDialog proxyDialog;
    private ToolBar pageToolBar;
    private JSObject browser;
    private String docsUrl;
    
    /**
     * Get the singleton instance of Ensemble
     * 
     * @return The singleton instance
     */
    public static Ensemble2 getEnsemble2() {
        return ensemble2;
    }
    
    /**
     * Start the application
     * 
     * @param stage The main application stage
     */
    @Override public void start(final Stage stage) {
        ensemble2 = this;
        stage.setTitle("Ensemble");
        // set default docs location
        try {
        docsUrl = System.getProperty("docs.url") != null ?
                System.getProperty("docs.url") : DEFAULT_DOCS_URL;
        } catch (SecurityException e1) {
            docsUrl = DEFAULT_DOCS_URL; // Don - Ignore this error - running in sandbox (TODO add a web proxy)
        }
        // create root stack pane that we use to be able to overlay proxy dialog
        StackPane layerPane = new StackPane();
        // check if applet
        try {
            browser = getHostServices().getWebContext();
            isApplet =  browser != null;
        } catch (Exception e) {
            isApplet = false;
        }
        if (!isApplet) {
            stage.initStyle(StageStyle.UNDECORATED);
            // create window resize button
            windowResizeButton = new WindowResizeButton(stage, 1020,700);
            // create root
            root = new BorderPane() {
                @Override protected void layoutChildren() {
                    super.layoutChildren();
                    windowResizeButton.autosize();
                    windowResizeButton.setLayoutX(getWidth() - windowResizeButton.getLayoutBounds().getWidth());
                    windowResizeButton.setLayoutY(getHeight() - windowResizeButton.getLayoutBounds().getHeight());
                }
            };
            root.getStyleClass().add("application");
        } else {
            root = new BorderPane();
            root.getStyleClass().add("applet");
        }
        root.setId("root");
        layerPane.setDepthTest(DepthTest.DISABLE);
        layerPane.getChildren().add(root);
        // create scene
        boolean is3dSupported = Platform.isSupported(ConditionalFeature.SCENE3D);
        scene = new Scene(layerPane, 1020, 700, is3dSupported);
        if (is3dSupported) {
            //RT-13234
            scene.setCamera(new PerspectiveCamera());
        }
        scene.getStylesheets().add(Ensemble2.class.getResource("ensemble2.css").toExternalForm());
        // create modal dimmer, to dim screen when showing modal dialogs
        modalDimmer = new StackPane();
        modalDimmer.setId("ModalDimmer");
        modalDimmer.setOnMouseClicked(new EventHandler() {
            public void handle(MouseEvent t) {
                t.consume();
                hideModalMessage();
            }
        });
        modalDimmer.setVisible(false);
        layerPane.getChildren().add(modalDimmer);
        // create main toolbar
        toolBar = new ToolBar();
        toolBar.setId("mainToolBar");
        ImageView logo = new ImageView(new Image(Ensemble2.class.getResourceAsStream("images/logo.png")));
        HBox.setMargin(logo, new Insets(0,0,0,5));
        toolBar.getItems().add(logo);
        Region spacer = new Region();
        HBox.setHgrow(spacer, Priority.ALWAYS);
        toolBar.getItems().add(spacer);
        Button highlightsButton = new Button();
        highlightsButton.setId("highlightsButton");
        highlightsButton.setMinSize(120, 66);
        highlightsButton.setPrefSize(120, 66);
        highlightsButton.setOnAction(new EventHandler() {
            @Override public void handle(ActionEvent event) {
                goToPage(Pages.HIGHLIGHTS);
            }
        });
        toolBar.getItems().add(highlightsButton);
        Button newButton = new Button();
        newButton.setId("newButton");
        newButton.setMinSize(120,66);
        newButton.setPrefSize(120,66);
        newButton.setOnAction(new EventHandler() {
            @Override public void handle(ActionEvent event) {
                goToPage(Pages.NEW);
            }
        });
        toolBar.getItems().add(newButton);
        Region spacer2 = new Region();
        HBox.setHgrow(spacer2, Priority.ALWAYS);
        toolBar.getItems().add(spacer2);
        ImageView searchTest = new ImageView(new Image(Ensemble2.class.getResourceAsStream("images/search-text.png")));
        toolBar.getItems().add(searchTest);
        SearchBox searchBox = new SearchBox();
        HBox.setMargin(searchBox, new Insets(0,5,0,0));
        toolBar.getItems().add(searchBox);
        toolBar.setPrefHeight(66);
        toolBar.setMinHeight(66);
        toolBar.setMaxHeight(66);
        GridPane.setConstraints(toolBar, 0, 0);
        if (!isApplet) {
            // add close min max
            final WindowButtons windowButtons = new WindowButtons(stage);
            toolBar.getItems().add(windowButtons);
            // add window header double clicking
            toolBar.setOnMouseClicked(new EventHandler() {
                @Override public void handle(MouseEvent event) {
                    if (event.getClickCount() == 2) {
                        windowButtons.toogleMaximized();
                    }
                }
            });
            // add window dragging
            toolBar.setOnMousePressed(new EventHandler() {
                @Override public void handle(MouseEvent event) {
                    mouseDragOffsetX = event.getSceneX();
                    mouseDragOffsetY = event.getSceneY();
                }
            });
            toolBar.setOnMouseDragged(new EventHandler() {
                @Override public void handle(MouseEvent event) {
                    if(!windowButtons.isMaximized()) {
                        stage.setX(event.getScreenX()-mouseDragOffsetX);
                        stage.setY(event.getScreenY()-mouseDragOffsetY);
                    }
                }
            });
        }
        // create page tree toolbar
        ToolBar pageTreeToolBar = new ToolBar() {
            @Override public void requestLayout() {
                super.requestLayout();
                // keep the height of pageToolBar in sync with pageTreeToolBar so they always match
                if (pageToolBar != null && getHeight() != pageToolBar.prefHeight(-1)) {
                    pageToolBar.setPrefHeight(getHeight());
                }
            }
        };
        pageTreeToolBar.setId("page-tree-toolbar");
        pageTreeToolBar.setMinHeight(29);
        pageTreeToolBar.setMaxWidth(Double.MAX_VALUE);
        ToggleGroup pageButtonGroup = new ToggleGroup();
        final ToggleButton allButton = new ToggleButton("All");
        allButton.setToggleGroup(pageButtonGroup);
        allButton.setSelected(true);
        final ToggleButton samplesButton = new ToggleButton("Samples");
        samplesButton.setToggleGroup(pageButtonGroup);
        final ToggleButton docsButton = new ToggleButton("Document");
        docsButton.setToggleGroup(pageButtonGroup);
        InvalidationListener treeButtonNotifyListener = new InvalidationListener() {
            public void invalidated(Observable ov) {
                if(allButton.isSelected()) {
                    pageTree.setRoot(pages.getRoot());
                } else if(samplesButton.isSelected()) {
                    pageTree.setRoot(pages.getSamples());
                } else if(docsButton.isSelected()) {
                    pageTree.setRoot(pages.getDocs());
                }
            }
        };
        allButton.selectedProperty().addListener(treeButtonNotifyListener);
        samplesButton.selectedProperty().addListener(treeButtonNotifyListener);
        docsButton.selectedProperty().addListener(treeButtonNotifyListener);
        pageTreeToolBar.getItems().addAll(allButton, samplesButton, docsButton);
        // create page tree
        pages = new Pages();
        proxyDialog = new ProxyDialog(stage, pages);
        proxyDialog.loadSettings();
        proxyDialog.getDocsInBackground(true,null);
        pages.parseSamples();
        pageTree = new TreeView();
        pageTree.setId("page-tree");
        pageTree.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        pageTree.setRoot(pages.getRoot());
        pageTree.setShowRoot(false);
        pageTree.setEditable(false);
        pageTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        pageTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observable, Object oldValue,
                    Object newValue) {
                if (!changingPage) {
                    Page selectedPage = (Page)pageTree.getSelectionModel().getSelectedItem();
                    if (selectedPage!=pages.getRoot()) goToPage(selectedPage);
                }
            }
        });
        // create left split pane
        BorderPane leftSplitPane = new BorderPane();
        leftSplitPane.setTop(pageTreeToolBar);
        leftSplitPane.setCenter(pageTree);
        // create page toolbar
        pageToolBar = new ToolBar();
        pageToolBar.setId("page-toolbar");
        pageToolBar.setMinHeight(29);
        pageToolBar.setMaxSize(Double.MAX_VALUE, Control.USE_PREF_SIZE);
        if (!isApplet) {
            Button backButton = new Button();
            backButton.setGraphic(new ImageView(new Image(Ensemble2.class.getResourceAsStream("images/back.png"))));
            backButton.setOnAction(new EventHandler() {
                public void handle(ActionEvent event) { back(); }
            });
            backButton.setMaxHeight(Double.MAX_VALUE);
            Button forwardButton = new Button();
            forwardButton.setGraphic(new ImageView(new Image(Ensemble2.class.getResourceAsStream("images/forward.png"))));
            forwardButton.setOnAction(new EventHandler() {
                public void handle(ActionEvent event) {
                    forward();
                }
            });
            forwardButton.setMaxHeight(Double.MAX_VALUE);
            Button reloadButton = new Button();
            reloadButton.setGraphic(new ImageView(new Image(Ensemble2.class.getResourceAsStream("images/reload.png"))));
            reloadButton.setOnAction(new EventHandler() {
                public void handle(ActionEvent event) {
                    reload();
                }
            });
            reloadButton.setMaxHeight(Double.MAX_VALUE);
            pageToolBar.getItems().addAll(backButton, forwardButton, reloadButton);
        }
        breadcrumbBar = new BreadcrumbBar();
        pageToolBar.getItems().add(breadcrumbBar);
        if (!isApplet) {
            Region spacer3 = new Region();
            HBox.setHgrow(spacer3, Priority.ALWAYS);
            Button settingsButton = new Button();
            settingsButton.setId("SettingsButton");
            settingsButton.setGraphic(new ImageView(new Image(Ensemble2.class.getResourceAsStream("images/settings.png"))));
            settingsButton.setOnAction(new EventHandler() {
                public void handle(ActionEvent event) {
                    showProxyDialog();
                }
            });
            settingsButton.setMaxHeight(Double.MAX_VALUE);
            pageToolBar.getItems().addAll(spacer3, settingsButton);
        }
        // create page area
        pageArea = new Pane() {
            @Override protected void layoutChildren() {
                for (Node child:pageArea.getChildren()) {
                    child.resizeRelocate(0, 0, pageArea.getWidth(), pageArea.getHeight());
                }
            }
        };
        pageArea.setId("page-area");
        // create right split pane
        BorderPane rightSplitPane = new BorderPane();
        rightSplitPane.setTop(pageToolBar);
        rightSplitPane.setCenter(pageArea);
        // create split pane
        splitPane = new SplitPane();
        splitPane.setId("page-splitpane");
        splitPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        GridPane.setConstraints(splitPane, 0, 1);
        splitPane.getItems().addAll(leftSplitPane, rightSplitPane);
        splitPane.setDividerPosition(0, 0.25);

        this.root.setTop(toolBar);
        this.root.setCenter(splitPane);
        // add window resize button so its on top
        if (!isApplet) {
            windowResizeButton.setManaged(false);
            this.root.getChildren().add(windowResizeButton);
        }
        // expand first level of the tree
        for (TreeItem child: pages.getRoot().getChildren()) {
            if (child == pages.getHighlighted() || child == pages.getNew()) continue;
            child.setExpanded(true);
            for (TreeItem child2: (ObservableList>)child.getChildren()) {
                child2.setExpanded(true);
            }
        }
        // goto initial page
        if (isApplet) {
            String hashLoc = getBrowserHashLocation();
            if (hashLoc != null) {
                goToPage(hashLoc);
            } else {
                // default to all samples
                goToPage(pages.getSamples());
            }
        } else {
            // default to all samples
            goToPage(pages.getSamples());
        }
        // show stage
        stage.setScene(scene);
        stage.show();
    }
    
    /**
     * Called from JavaScript in the browser when the page hash location changes
     * 
     * @param hashLoc The new has location, e.g. #SAMPLES
     */
    public void hashChanged(String hashLoc) {
        if (hashLoc != null) {
            // remove # 
            if (hashLoc.length() == 0) {
                hashLoc = null;
            } else {
                hashLoc = hashLoc.substring(1);
            }
            // if new page then navigate to it
            final String path = hashLoc;
            Platform.runLater(new Runnable() {
                public void run() {
                    if (!path.equals(currentPagePath)) goToPage(path);
                }
            });
        }
    }
    
    /**
     * Get the URL of the java doc root directory being used to get 
     * documentation from
     * 
     * @return Documentation directory URL
     */
    public String getDocsUrl() {
        return docsUrl;
    }

    /**
     * Set the URL of the java doc root directory being used to get 
     * documentation from
     * 
     * @param docsUrl Documentation directory URL
     */
    public void setDocsUrl(String docsUrl) {
        this.docsUrl = docsUrl;
    }
    
    /**
     * Fetch the current hash location from the browser via JavaScript
     * 
     * @return Current browsers hash location
     */
    private String getBrowserHashLocation() {
        String hashLoc = null;
        try {
            hashLoc = (String)browser.eval("window.location.hash");
        } catch (Exception e) {
            try {
                System.out.println("Warning failed to get browser location, retrying...");
                hashLoc = (String)browser.eval("window.location.hash");
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        // remove #
        if (hashLoc != null) {
            if (hashLoc.length() == 0) {
                hashLoc = null;
            } else {
                hashLoc = hashLoc.substring(1);
            }
        }
        return hashLoc;
    }
    
    /**
     * Show the given node as a floating dialog over the whole application, with 
     * the rest of the application dimmed out and blocked from mouse events.
     * 
     * @param message 
     */
    public void showModalMessage(Node message) {
        modalDimmer.getChildren().add(message);
        modalDimmer.setOpacity(0);
        modalDimmer.setVisible(true);
        modalDimmer.setCache(true);
        TimelineBuilder.create().keyFrames(
            new KeyFrame(Duration.seconds(1), 
                new EventHandler() {
                    public void handle(ActionEvent t) {
                        modalDimmer.setCache(false);
                    }
                },
                new KeyValue(modalDimmer.opacityProperty(),1, Interpolator.EASE_BOTH)
        )).build().play();
    }
    
    /**
     * Hide any modal message that is shown
     */
    public void hideModalMessage() {
        modalDimmer.setCache(true);
        TimelineBuilder.create().keyFrames(
            new KeyFrame(Duration.seconds(1), 
                new EventHandler() {
                    public void handle(ActionEvent t) {
                        modalDimmer.setCache(false);
                        modalDimmer.setVisible(false);
                        modalDimmer.getChildren().clear();
                    }
                },
                new KeyValue(modalDimmer.opacityProperty(),0, Interpolator.EASE_BOTH)
        )).build().play();
    }
    
    /**
     * Get the pages object that contains the tree of all avaliable pages
     * 
     * @return Pages containing tree of all pages
     */
    public Pages getPages() {
        return pages;
    }

    /**
     * Change to new page without swapping views, assumes that the current view 
     * is already showing the new page
     * 
     * @param page The new page object
     */
    public void updateCurrentPage(Page page) {
        goToPage(page, true,false,false);
    }
    
    /**
     * Take ensemble to the given page path, navigating there and adding 
     * current page to history
     * 
     * @param pagePath The path for the new page to show
     */
    public void goToPage(String pagePath) {
        goToPage(pages.getPage(pagePath));
    }

    /**
     * Take ensemble to the given page path, navigating there and adding 
     * current page to history.
     * 
     * @param pagePath  The path for the new page to show
     * @param force     Reload page even if its the current page
     */
    public void goToPage(String pagePath, boolean force) {
        goToPage(pages.getPage(pagePath),true,force,true);
    }

    /**
     * Take ensemble to the given page object, navigating there and adding 
     * current page to history.
     * 
     * @param page Page object to show
     */
    public void goToPage(Page page) {
        goToPage(page, true, false, true);
    }
    
    /**
     * Take ensemble to the given page object, navigating there.
     * 
     * @param page          Page object to show
     * @param addHistory    When true add current page to history before navigating
     * @param force         When true reload page if page is current page
     * @param swapViews     If view should be swapped to new page
     */
    private void goToPage(Page page, boolean addHistory, boolean force, boolean swapViews) {
        if(page==null) return;
        if(!force && page == currentPage) {
            return;
        }
        changingPage = true;
        if (swapViews) {
            Node view = page.createView();
            if (view == null) view = new Region(); // todo temp workaround
            // replace view in pageArea if new
            if (force || view != currentPageView) {
                for (Node child:pageArea.getChildren()){
                    if (child instanceof SamplePage.SamplePageView) {
                        ((SamplePage.SamplePageView)child).stop();
                    }
                }
                pageArea.getChildren().setAll(view);
                currentPageView = view;
            }
        }
        // add page to history
        if (addHistory && currentPage!=null) {
            history.push(currentPage);
            forwardHistory.clear();
        }
        currentPage = page;
        currentPagePath = page.getPath();
        // when in applet update location bar
        if (isApplet) {
            try {
                browser.eval("window.location.hash='"+currentPagePath+"';");
            } catch (Exception e) {
                try {
                    System.out.println("Warning failed to set browser location, retrying...");
                    browser.eval("window.location.hash='"+currentPagePath+"';");
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
        }
        // expand tree to selected node
        Page p = page;
        while (p!=null) {
            p.setExpanded(true);
            p = (Page) p.getParent();
        }
        // update tree selection
        pageTree.getSelectionModel().select(page);
        // update breadcrumb bar
        breadcrumbBar.setPath(currentPagePath);
        // done
        changingPage = false;
    }
    
    /**
     * Check if current call stack was from back or forward button's action
     * 
     * @return True if current call was caused by action on back or forward button
     */
    public boolean isFromForwardOrBackButton() {
        return fromForwardOrBackButton;
    }
    
    /**
     * Got to previous page in history
     */
    public void back() {
        fromForwardOrBackButton = true;
        if (!history.isEmpty()) {      
            Page prevPage = history.pop();
            forwardHistory.push(currentPage);
            goToPage(prevPage,false, false, true);
        }
        fromForwardOrBackButton = false;
    }
    
    /**
     * Utility method for viewing the page history
     */
    private void printHistory() {
        System.out.print("   HISTORY = ");
        for (Page page:history) {
            System.out.print(page.getName()+"->");
        }
        System.out.print("   ["+currentPage.getName()+"]");
        for (Page page:forwardHistory) {
            System.out.print(page.getName()+"->");
        }
        System.out.print("\n");
    }

    /**
     * Go to next page in history if there is one
     */
    public void forward() {
        fromForwardOrBackButton = true;
        if (!forwardHistory.isEmpty()) {
            Page prevPage = forwardHistory.pop();
            history.push(currentPage);
            goToPage(prevPage,false, false, true);
        }
        fromForwardOrBackButton = false;
    }
    
    /**
     * Reload the current page
     */
    public void reload() {
        goToPage(currentPage,false,true, true);
    }
    
    /**
     * Show the dialog for setting proxy to the user
     */
    public void showProxyDialog() {
        showModalMessage(proxyDialog);
    }
    
    /**
     * Java Main Method for launching application when not using JavaFX 
     * Launcher, eg from IDE without JavaFX support
     * 
     * @param args Command line arguments
     */
    public static void main(String[] args) {
        Application.launch(args);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy