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

org.jacpfx.rcp.workbench.AFXWorkbench Maven / Gradle / Ivy

There is a newer version: 2.1
Show newest version
/************************************************************************
 *
 * Copyright (C) 2010 - 2014
 *
 * [AFX2Workbench.java]
 * JACPFX Project (https://github.com/JacpFX/JacpFX/)
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 *
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
 * express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 *
 *
 ************************************************************************/
package org.jacpfx.rcp.workbench;

import javafx.application.Platform;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ToolBar;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.jacpfx.api.annotations.workbench.Workbench;
import org.jacpfx.api.component.Injectable;
import org.jacpfx.api.component.Perspective;
import org.jacpfx.api.component.RootComponent;
import org.jacpfx.api.componentLayout.WorkbenchLayout;
import org.jacpfx.api.context.JacpContext;
import org.jacpfx.api.coordinator.Coordinator;
import org.jacpfx.api.delegator.ComponentDelegator;
import org.jacpfx.api.delegator.MessageDelegator;
import org.jacpfx.api.handler.ComponentHandler;
import org.jacpfx.api.launcher.Launcher;
import org.jacpfx.api.message.Message;
import org.jacpfx.api.util.OS;
import org.jacpfx.api.util.ToolbarPosition;
import org.jacpfx.api.workbench.Base;
import org.jacpfx.rcp.componentLayout.FXComponentLayout;
import org.jacpfx.rcp.componentLayout.FXWorkbenchLayout;
import org.jacpfx.rcp.components.managedFragment.ManagedFragment;
import org.jacpfx.rcp.components.modalDialog.JACPModalDialog;
import org.jacpfx.rcp.components.toolBar.JACPToolBar;
import org.jacpfx.rcp.context.Context;
import org.jacpfx.rcp.context.JacpContextImpl;
import org.jacpfx.rcp.coordinator.MessageCoordinator;
import org.jacpfx.rcp.delegator.MessageDelegatorImpl;
import org.jacpfx.rcp.handler.PerspectiveHandlerImpl;
import org.jacpfx.rcp.message.MessageImpl;
import org.jacpfx.rcp.perspective.AFXPerspective;
import org.jacpfx.rcp.registry.PerspectiveRegistry;
import org.jacpfx.rcp.util.*;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * represents the basic JavaFX2 workbench instance; handles perspective and
 * component;
 *
 * @author Andy Moncsek, Patrick Symmangk
 */
public abstract class AFXWorkbench
        implements
        Base, Event, Object>,
        RootComponent, Event, Object>, Message> {

    private final ComponentDelegator, Event, Object> componentDelegator = new org.jacpfx.rcp.delegator.ComponentDelegator();
    private final MessageDelegator, Event, Object> messageDelegator = new MessageDelegatorImpl();
    private final WorkbenchLayout workbenchLayout = new FXWorkbenchLayout();
    private final Logger logger = Logger.getLogger(this.getClass().getName());
    private List, Event, Object>> perspectives;
    private ComponentHandler, Event, Object>, Message> componentHandler;
    private Coordinator, Event, Object> messageCoordinator;
    private Launcher launcher;
    private Stage stage;
    private GridPane root;
    private BorderPane baseLayoutPane;
    private StackPane absoluteRoot;
    private GridPane base;
    private Pane glassPane;
    private JACPModalDialog dimmer;
    private Context context;
    private FXWorkbench handle;

    /**
     * JavaFX specific start sequence
     *
     * @param stage, The JavaFX stage
     */
    private void start(final Stage stage) {
        this.stage = stage;
        DimensionUtil.init(stage);
        this.registerTeardownActions();
        this.log("1: init workbench");

        initWorkbenchHandle(stage);

        this.log("3: handle initialisation sequence");
        this.perspectives = WorkbenchUtil.getInstance(launcher).createPerspectiveInstances(getWorkbenchAnnotation());
        if (perspectives == null) return;

        this.initSubsystem();
        this.handleInitialisationSequence();
    }

    private void initWorkbenchHandle(final Stage stage) {
        // init user defined workspace
        this.handle.handleInitialLayout(new MessageImpl(this.context.getId(), "init"),
                this.getWorkbenchLayout(), stage);
        this.setBasicLayout(stage);

        this.handle.postHandle(new FXComponentLayout(this.getWorkbenchLayout()
                .getMenu(), this.glassPane, null, this.getContext().getId()));
    }

    private void registerTeardownActions() {
        TearDownHandler.registerBase(this);
        stage.setOnCloseRequest(arg0 -> {
            ShutdownThreadsHandler.shutdowAll();
            TearDownHandler.handleGlobalTearDown();
            Platform.exit();
        });
    }

    private void initSubsystem() {
        this.componentHandler = new PerspectiveHandlerImpl(this.launcher,
                this.workbenchLayout, this.root);
        this.messageCoordinator.setPerspectiveHandler(this.componentHandler);
        this.componentDelegator.setPerspectiveHandler(this.componentHandler);
        this.messageDelegator.setPerspectiveHandler(this.componentHandler);
    }

    @Override
    /**
     * {@inheritDoc}
     */
    public void init(final Launcher launcher, Object root) {
        this.launcher = launcher;
        ManagedFragment.initManagedFragment(launcher);
        final Workbench annotation = getWorkbenchAnnotation();
        this.messageCoordinator = new MessageCoordinator(annotation.id(), this.launcher);
        this.messageCoordinator.setDelegateQueue(this.messageDelegator.getMessageDelegateQueue());
        this.context = new JacpContextImpl(annotation.id(), annotation.name(), this.messageCoordinator.getMessageQueue());
        FXUtil.performResourceInjection(this.handle, this.context);
        start(Stage.class.cast(root));
        GlobalMediator.getInstance().handleWorkbenchToolBarButtons(annotation.id(), true);
        logger.info("INIT");
    }

    private Workbench getWorkbenchAnnotation() {
        return this.handle.getClass().getAnnotation(Workbench.class);
    }

    @Override
    /**
     * {@inheritDoc}
     */
    public final void initComponents(final Message action) {
        this.perspectives.forEach(this::initPerspective);
        final List, Event, Object>> activeSequentialPerspectiveList = this.perspectives
                .stream()
                .sequential()
                .filter(p -> p.getContext() != null && p.getContext().isActive())
                .collect(Collectors.toList());
        if (!activeSequentialPerspectiveList.isEmpty()) {
            GlobalMediator.getInstance().handleToolBarButtons(activeSequentialPerspectiveList.get(activeSequentialPerspectiveList.size() - 1), true);
        }

    }

    private void initPerspective(Perspective, Event, Object> perspective) {
        this.registerComponent(perspective);
        this.log("3.4.1: register component: " + perspective.getContext().getName());
        final CountDownLatch waitForInit = new CountDownLatch(1);
        this.log("3.4.2: init perspective");
        if (perspective.getContext().isActive()) {
            final Runnable r = () -> {
                AFXWorkbench.this.componentHandler.initComponent(
                        new MessageImpl(perspective.getContext().getId(), perspective
                                .getContext().getId(), "init", null), perspective);
                waitForInit.countDown();
            };
            if (Platform.isFxApplicationThread()) {
                r.run();
            } else {
                Platform.runLater(r);

            }
            try {
                // wait for possible async execution
                waitForInit.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * handles sequence for workbench size, menu bar, tool bar and perspective
     * initialisation
     */
    private void handleInitialisationSequence() {
        AFXWorkbench.this.stage.show();
        // start perspective Observer worker thread
        // TODO create status daemon which observes
        // thread component on
        // failure and restarts if needed!!
        ((Thread) AFXWorkbench.this.messageCoordinator)
                .start();
        ((Thread) AFXWorkbench.this.componentDelegator)
                .start();
        ((Thread) AFXWorkbench.this.messageDelegator)
                .start();
        // handle perspective
        AFXWorkbench.this.log("3.3: workbench init perspective");
        AFXWorkbench.this.initComponents(null);
    }

    @Override
    /**
     * {@inheritDoc}
     */
    public final void registerComponent(
            final Perspective, Event, Object> perspective) {
        final String perspectiveId = PerspectiveUtil.getPerspectiveIdFromAnnotation(perspective);
        final MessageCoordinator messageCoordinatorLocal = new MessageCoordinator(perspectiveId, this.launcher);
        messageCoordinatorLocal.setDelegateQueue(this.messageDelegator.getMessageDelegateQueue());
        messageCoordinatorLocal.setPerspectiveHandler(this.componentHandler);
        // use compleatableFuture
        perspective.init(this.componentDelegator.getComponentDelegateQueue(),
                this.messageDelegator.getMessageDelegateQueue(),
                messageCoordinatorLocal, this.launcher);
        messageCoordinatorLocal.start();
        WorkbenchUtil.handleMetaAnnotation(perspective, this.getWorkbenchAnnotation().id());
        addComponent(perspective);
    }

    @Override
    public final void addComponent(
            final Perspective, Event, Object> perspective) {
        PerspectiveRegistry.registerPerspective(perspective);
    }

    @Override
    /**
     * {@inheritDoc}
     */
    // TODO remove this!!
    public final void unregisterComponent(
            final Perspective, Event, Object> perspective) {
        FXUtil.setPrivateMemberValue(AFXPerspective.class, perspective,
                FXUtil.APERSPECTIVE_MQUEUE, null);
        PerspectiveRegistry.removePerspective(perspective);
    }

    @Override
    public final void removeAllCompnents() {
        this.perspectives.forEach(this::unregisterComponent);
        this.perspectives.clear();
    }

    /**
     * {@inheritDoc}
     */
    private FXWorkbenchLayout getWorkbenchLayout() {
        return (FXWorkbenchLayout) this.workbenchLayout;
    }

    @Override
    public ComponentHandler, Event, Object>, Message> getComponentHandler() {
        return this.componentHandler;
    }

    @Override
    /**
     * {@inheritDoc}
     */
    public final List, Event, Object>> getPerspectives() {
        return this.perspectives;
    }

    /**
     * set basic layout manager for workspace
     *
     * @param stage javafx.stage.Stage
     */

    // TODO: handle the custom decorator
    private void setBasicLayout(final Stage stage) {
        // the top most pane is a Stackpane
        this.base = new GridPane();
        this.absoluteRoot = new StackPane();
        this.baseLayoutPane = new BorderPane();
        this.stage = stage;

        if (OS.MAC.equals(OS.getOS())) {
            // OSX will always be DECORATED due to fullscreen option!
            stage.initStyle(StageStyle.DECORATED);
        } else {
            stage.initStyle(this.getWorkbenchLayout().getStyle());
        }

        this.initBaseLayout();
        this.initMenuLayout();
        this.initToolbarLayout();
        this.completeLayout();
    }

    private void completeLayout() {
        // fetch current workbenchsize
        final int x = this.getWorkbenchLayout().getWorkbenchSize().getX();
        final int y = this.getWorkbenchLayout().getWorkbenchSize().getY();

        this.absoluteRoot.getChildren().add(this.baseLayoutPane);
        this.absoluteRoot.setId(CSSUtil.CSSIdConstants.ID_ROOT);
        this.base.getChildren().add(absoluteRoot);
        LayoutUtil.GridPaneUtil.setFullGrow(Priority.ALWAYS, this.absoluteRoot);
        this.stage.setScene(new Scene(this.base, x, y));
        this.initCSS(this.stage.getScene());
        SceneUtil.setScene(this.stage.getScene());
        this.absoluteRoot.getChildren().add(this.glassPane);
        this.absoluteRoot.getChildren().add(this.dimmer);

        this.initGlobalMouseEvents();

    }

    private void initGlobalMouseEvents() {
        // catch global clicks
        this.stage.getScene().addEventFilter(
                MouseEvent.MOUSE_RELEASED,
                (event) -> {
                    GlobalMediator.getInstance().hideAllHideables(event);

                });
    }

    private void initMenuLayout() {
        // add the menu if needed
        if (this.getWorkbenchLayout().isMenuEnabled()) {
            this.baseLayoutPane.setTop(this.getWorkbenchLayout().getMenu());
            this.getWorkbenchLayout().getMenu().setMenuDragEnabled(stage);
        }

    }

    private void initToolbarLayout() {
        // add toolbars in a specific order
        if (!this.getWorkbenchLayout().getRegisteredToolBars().isEmpty()) {

            // add another Layer to hold all the toolbars
            final BorderPane toolbarPane = new BorderPane();
            this.baseLayoutPane.setCenter(toolbarPane);

            final Map registeredToolbars = this
                    .getWorkbenchLayout().getRegisteredToolBars();

            for (Iterator> iterator = registeredToolbars
                    .entrySet().iterator(); iterator.hasNext(); ) {
                Entry entry = iterator.next();
                final ToolbarPosition position = entry.getKey();
                final JACPToolBar toolBar = entry.getValue();
                this.assignCorrectToolBarLayout(position, toolBar, toolbarPane);
            }

            // add root to the center
            toolbarPane.setCenter(this.root);
            toolbarPane.getStyleClass().add(
                    CSSUtil.CSSClassConstants.CLASS_DARK_BORDER);

        } else {
            // no toolbars -> no special Layout needed
            this.baseLayoutPane.setCenter(this.root);
        }

    }

    private void initCSS(final Scene scene) {
        scene.getStylesheets().addAll(
                AFXWorkbench.class.getResource("/styles/jacp-styles.css")
                        .toExternalForm());
    }

    private void initBaseLayout() {
        this.initRootPane();
        this.initDimmer();
        this.initGlassPane();
    }

    private void initRootPane() {
        // root is top most pane
        this.root = new GridPane();
        this.root.setCache(true);
        this.root.setId(CSSUtil.CSSIdConstants.ID_ROOT_PANE);

    }

    private void initDimmer() {
        JACPModalDialog.initDialog(this.baseLayoutPane);
        this.dimmer = JACPModalDialog.getInstance();
        this.dimmer.setVisible(false);
    }

    private void initGlassPane() {
        // Pane for custom elements added to the glasspane
        this.glassPane = this.getWorkbenchLayout().getGlassPane();
        this.glassPane.autosize();
        this.glassPane.setVisible(false);
        this.glassPane.setPrefSize(0, 0);
    }

    /**
     * set toolBars to correct position
     *
     * @param position, The toolbar position
     * @param bar,      the affected toolbar
     * @param pane,     the root pane
     */
    private void assignCorrectToolBarLayout(final ToolbarPosition position,
                                            final ToolBar bar, final BorderPane pane) {
        switch (position) {
            case NORTH:
                pane.setTop(bar);
                break;
            case SOUTH:
                pane.setBottom(bar);
                break;
            case EAST:
                bar.setOrientation(Orientation.VERTICAL);
                pane.setRight(bar);
                break;
            case WEST:
                bar.setOrientation(Orientation.VERTICAL);
                pane.setLeft(bar);
                break;
        }
    }

    private void log(final String message) {
        if (this.logger.isLoggable(Level.FINE)) {
            this.logger.fine(">> " + message);
        }
    }

    @Override
    public JacpContext getContext() {
        return context;
    }

    private FXWorkbench getWorkbenchHandle() {
        return handle;
    }

    @SuppressWarnings("unchecked")
    @Override
    public FXWorkbench getComponentHandle() {
        return this.handle;
    }

    @Override
    public  void setComponentHandle(X handle) {
        this.handle = (FXWorkbench) handle;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy