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

org.jacpfx.rcp.handler.PerspectiveHandlerImpl Maven / Gradle / Ivy

There is a newer version: 2.1
Show newest version
/************************************************************************
 *
 * Copyright (C) 2010 - 2014
 *
 * [FX2WorkbenchHandler.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.handler;

import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import org.jacpfx.api.annotations.lifecycle.OnHide;
import org.jacpfx.api.annotations.lifecycle.OnShow;
import org.jacpfx.api.annotations.lifecycle.PostConstruct;
import org.jacpfx.api.annotations.lifecycle.PreDestroy;
import org.jacpfx.api.component.Perspective;
import org.jacpfx.api.component.PerspectiveView;
import org.jacpfx.api.component.SubComponent;
import org.jacpfx.api.component.UIComponent;
import org.jacpfx.api.componentLayout.PerspectiveLayoutInterface;
import org.jacpfx.api.componentLayout.WorkbenchLayout;
import org.jacpfx.api.launcher.Launcher;
import org.jacpfx.api.message.Message;
import org.jacpfx.api.util.UIType;
import org.jacpfx.rcp.component.AFXComponent;
import org.jacpfx.rcp.component.ASubComponent;
import org.jacpfx.rcp.componentLayout.FXComponentLayout;
import org.jacpfx.rcp.componentLayout.FXMLPerspectiveLayout;
import org.jacpfx.rcp.componentLayout.FXPerspectiveLayout;
import org.jacpfx.rcp.componentLayout.FXWorkbenchLayout;
import org.jacpfx.rcp.context.JacpContextImpl;
import org.jacpfx.rcp.message.MessageImpl;
import org.jacpfx.rcp.perspective.AFXPerspective;
import org.jacpfx.rcp.registry.PerspectiveRegistry;
import org.jacpfx.rcp.util.FXUtil;
import org.jacpfx.rcp.util.TearDownHandler;
import org.jacpfx.rcp.workbench.GlobalMediator;

import java.net.URL;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Handles initialization and update of perspective in a workbench.
 *
 * @author Andy Moncsek
 */
public class PerspectiveHandlerImpl implements
        org.jacpfx.api.handler.ComponentHandler, Event, Object>, Message> {
    private final Logger logger = Logger.getLogger(this.getClass().getName());
    private final WorkbenchLayout workbenchLayout;
    private final Launcher launcher;
    private final GridPane root;


    public PerspectiveHandlerImpl(final Launcher launcher, final WorkbenchLayout workbenchLayout,
                                  final GridPane root) {
        this.workbenchLayout = workbenchLayout;
        this.root = root;
        this.launcher = launcher;
    }

    @Override
    public final void handleAndReplaceComponent(final Message action,
                                                final Perspective, Event, Object> perspective) {
        final PerspectiveLayoutInterface perspectiveLayout = ((AFXPerspective) perspective)
                .getIPerspectiveLayout();
        // backup old component
        final Node componentOld = this.getLayoutComponentFromPerspectiveLayout(perspectiveLayout);
        perspective.handlePerspective(action);
        if (!perspective.getContext().isActive()) {
            handleInactivePerspective(perspective, perspectiveLayout, componentOld);
        } else {
            handleActivePerspective(perspective, perspectiveLayout, componentOld);
        }

    }

    private void handleActivePerspective(final Perspective, Event, Object> perspective, final PerspectiveLayoutInterface perspectiveLayout, final Node componentOld) {
        if (componentOld != null) {
            this.handlePerspectiveReassignment(perspective, perspectiveLayout, componentOld);
        } // End outer if
        else {
            this.initPerspectiveUI(perspectiveLayout);
        } // End else
    }

    private void handleInactivePerspective(final Perspective, Event, Object> perspective, final PerspectiveLayoutInterface perspectiveLayout, final Node componentOld) {
        // 3 possible variants
        // 1 only one Perspective which is deactivated:  remove...
        // 2 second active perspective available, current perspective is the one which is disabled: find the other perspective, handle OnShow, add not to workbench
        // 3 second perspective is available, other perspective is currently displayed: turn off the perspective

        FXUtil.invokeHandleMethodsByAnnotation(PreDestroy.class, perspective.getPerspective(), perspectiveLayout, JacpContextImpl.class.cast(perspective.getContext()).getComponentLayout(), perspective.getContext().getResourceBundle());
        removePerspectiveNodeFromWorkbench(perspectiveLayout, componentOld);
        displayNextPossiblePerspective(perspective);
        shutDownAndClearComponents(perspective);
    }

    private void shutDownAndClearComponents(final Perspective, Event, Object> perspective) {
        final List, Event, Object>> componentsToShutdown = perspective.getSubcomponents();
        componentsToShutdown.stream()
                .filter(c -> c.getContext().isActive())
                .forEach(this::shutDownComponent);
        perspective.removeAllCompnents();
    }

    private void shutDownComponent(final SubComponent, Event, Object> component) {
        if (AFXComponent.class.isAssignableFrom(component.getClass())) {
            TearDownHandler.shutDownFXComponent(AFXComponent.class.cast(component), component.getParentId());
        } else {
            TearDownHandler.shutDownAsyncComponent(ASubComponent.class.cast(component));
        }
    }

    private void displayNextPossiblePerspective(final Perspective, Event, Object> current) {
        final Perspective, Event, Object> possiblePerspectiveToShow = PerspectiveRegistry.findNextActivePerspective(current);
        if (possiblePerspectiveToShow != null) {
            final String possiblePerspectiveId = possiblePerspectiveToShow.getContext().getId();
            final String perspectiveIdBefore = PerspectiveRegistry.getAndSetCurrentVisiblePerspective(possiblePerspectiveToShow.getContext().getId());
            if (!possiblePerspectiveId.equals(perspectiveIdBefore)) {
                final PerspectiveLayoutInterface perspectiveLayoutReplacementComponent = ((AFXPerspective) possiblePerspectiveToShow)
                        .getIPerspectiveLayout();
                final PerspectiveView, Event, Object> perspectiveView = ((PerspectiveView, Event, Object>) possiblePerspectiveToShow);
                // execute OnShow
                FXUtil.invokeHandleMethodsByAnnotation(OnShow.class, possiblePerspectiveToShow.getPerspective(), perspectiveLayoutReplacementComponent,
                        perspectiveView.getType().equals(UIType.DECLARATIVE) ? perspectiveView.getDocumentURL() : null, possiblePerspectiveToShow.getContext().getResourceBundle());
                this.handlePerspectiveReassignment(possiblePerspectiveToShow, perspectiveLayoutReplacementComponent, this.getLayoutComponentFromPerspectiveLayout(perspectiveLayoutReplacementComponent));
            }

        }
    }

    private void removePerspectiveNodeFromWorkbench(final PerspectiveLayoutInterface perspectiveLayout, final Node componentOld) {
        this.root.setCacheHint(CacheHint.SPEED);
        final Node nodeToRemove = componentOld != null ? componentOld : this.getLayoutComponentFromPerspectiveLayout(perspectiveLayout);
        FXUtil.getChildren(nodeToRemove).clear();
        this.root.getChildren().remove(nodeToRemove);
        this.root.setCacheHint(CacheHint.DEFAULT);
    }

    @Override
    public final void initComponent(final Message message,
                                    final Perspective, Event, Object> perspective) {
        final Thread t = Thread.currentThread();
        try {
            this.log("3.4.3: perspective handle init");
            FXUtil.performResourceInjection(perspective.getPerspective(), perspective.getContext());

            this.handlePerspectiveInitMethod(message, perspective);
            this.log("3.4.5: perspective init bar entries");
            final PerspectiveLayoutInterface perspectiveLayout = ((AFXPerspective) perspective)
                    .getIPerspectiveLayout();
            this.initPerspectiveUI(perspectiveLayout);
            PerspectiveRegistry.getAndSetCurrentVisiblePerspective(perspective.getContext().getId());
            this.log("3.4.4: perspective init subcomponents");
            perspective.initComponents(message);
        } catch (Exception e) {
            t.getUncaughtExceptionHandler().uncaughtException(t, e);
        }

    }

    /**
     * reassignment can only be done in FX main thread;
     */
    private void handlePerspectiveReassignment(final Perspective, Event, Object> perspective,
                                               final PerspectiveLayoutInterface perspectiveLayout, final Node componentOld) {
        this.root.setCacheHint(CacheHint.SPEED);
        final Node componentNew = this.getLayoutComponentFromPerspectiveLayout(perspectiveLayout);
        componentNew.setCache(true);
        this.reassignChild(perspective, componentOld, componentNew);
        // set already active editors to new component
        this.reassignSubcomponents(perspective, perspectiveLayout);
        this.root.setCacheHint(CacheHint.DEFAULT);
    }

    /**
     * add all active subcomponents to current perspective
     *
     * @param perspective, The perspective where to reassign
     * @param layout,      The perspective layout
     */
    private void reassignSubcomponents(final Perspective, Event, Object> perspective,
                                       final PerspectiveLayoutInterface layout) {
        final List, Event, Object>> subcomponents = perspective.getSubcomponents();
        if (subcomponents == null)
            return;
        subcomponents.forEach(subComp -> {
            if (subComp instanceof AFXComponent && subComp.getContext().isActive()) {
                final AFXComponent subComponent = (AFXComponent) subComp;
                this.addComponentByType(subComponent, layout);
            } // End outer if
        });

    }

    /**
     * find valid target and add type specific new ui component
     *
     * @param component, The ui component to add
     * @param layout,    The perspective layout
     */
    private void addComponentByType(final UIComponent, Event, Object> component,
                                    final PerspectiveLayoutInterface layout) {
        final String targetLayout = JacpContextImpl.class.cast(component.getContext()).getTargetLayout();
        final Node validContainer = layout.getTargetLayoutComponents().get(targetLayout);
        final ObservableList children = FXUtil.getChildren(validContainer);
        final Node currentRoot = component.getRoot();
        if (children == null || currentRoot == null) return;
        int index = children.indexOf(currentRoot);
        if (index < 0) {
            addNewRoot(children, currentRoot);
        } else {
            bringRootToFront(index, children, currentRoot);
        }

    }

    private void bringRootToFront(int index, final ObservableList children, final Node root) {
        if (index != 0) {
            children.remove(index);
            GridPane.setHgrow(root, Priority.ALWAYS);
            GridPane.setVgrow(root, Priority.ALWAYS);
            children.set(0, root);
        }
    }

    private void addNewRoot(final ObservableList children, final Node root) {
        GridPane.setHgrow(root, Priority.ALWAYS);
        GridPane.setVgrow(root, Priority.ALWAYS);
        children.add(root);
    }

    /**
     * handle reassignment of component in perspective ui
     *
     * @param perspective, The current perspective
     * @param oldComp,     The old component Node
     * @param newComp,     The new component Node
     */
    private void reassignChild(final Perspective, Event, Object> perspective, final Node oldComp, final Node newComp) {
        final ObservableList children = this.root.getChildren();
        final Perspective, Event, Object> previousPerspective = getPreviousPerspective(perspective);

        hideChildrenAndExecuteOnHide(perspective, previousPerspective, children);
        // hide all buttons of the previous perspective
        GlobalMediator.getInstance().handleToolBarButtons(previousPerspective, false);
        executeOnShow(perspective, previousPerspective);
        replaceRootNodes(children, oldComp, newComp);
        // show all buttons of the new perspective
        GlobalMediator.getInstance().handleToolBarButtons(perspective, true);
        newComp.setVisible(true);
    }

    private void replaceRootNodes(final ObservableList children, final Node oldComp, final Node newComp) {
        if (!oldComp.equals(newComp)) {
            children.remove(oldComp);
            children.add(newComp);
        }
    }

    private void executeOnShow(final Perspective, Event, Object> perspective, final Perspective, Event, Object> previousPerspective) {
        if (!perspective.equals(previousPerspective)) {
            final PerspectiveView, Event, Object> perspectiveView = ((PerspectiveView, Event, Object>) perspective);
            final FXComponentLayout layout = new FXComponentLayout(this.getWorkbenchLayout(), null, perspective.getContext().getId());
            // execute OnShow
            FXUtil.invokeHandleMethodsByAnnotation(OnShow.class, perspective.getPerspective(), layout,
                    perspectiveView.getType().equals(UIType.DECLARATIVE) ? perspectiveView.getDocumentURL() : null, perspectiveView.getContext().getResourceBundle());

        }
    }

    /**
     * returns the previous visible perspective
     *
     * @param perspective, The current perspective
     * @return Returns the previous visible perspective
     */
    private Perspective, Event, Object> getPreviousPerspective(final Perspective, Event, Object> perspective) {
        final String previousId = PerspectiveRegistry.getAndSetCurrentVisiblePerspective(perspective.getContext().getId());
        if (previousId == null) return perspective;
        return perspective.getContext().getId().equals(previousId) ? perspective : PerspectiveRegistry.findPerspectiveById(previousId);
    }

    /**
     * add perspective UI to workbench root component
     *
     * @param perspectiveLayout, The perspective layout
     */
    private void initPerspectiveUI(final PerspectiveLayoutInterface perspectiveLayout) {
        this.log("3.4.6: perspective init SINGLE_PANE");
        final Node comp = this.getLayoutComponentFromPerspectiveLayout(perspectiveLayout);
        if (comp != null) {
            comp.setVisible(true);
            comp.setCache(true);
            final ObservableList children = this.root.getChildren();
            this.hideChildren(children);
            GridPane.setConstraints(comp, 0, 0);
            children.add(comp);
        }
    }

    private void handlePerspectiveInitMethod(final Message action,
                                             final Perspective, Event, Object> perspective) {
        handlePerspectiveView(perspective);

        // set perspective to active
        JacpContextImpl.class.cast(perspective.getContext()).setActive(true);
        if (FXUtil.getTargetPerspectiveId(action.getTargetId()).equals(perspective.getContext().getId())) {
            this.log("3.4.3.1: perspective handle with custom message");
            perspective.handlePerspective(action);
        } // End if
        else {
            this.log("3.4.3.1: perspective handle with default >>init<< message");
            perspective.handlePerspective(new MessageImpl(perspective.getContext().getId(), perspective.getContext().getId(), "init", null));
        } // End else
    }

    private void handlePerspectiveView(final Perspective, Event, Object> perspective) {
        if (perspective instanceof PerspectiveView) {
            final JacpContextImpl context = JacpContextImpl.class.cast(perspective.getContext());
            initFXComponentLayout(context);

            final PerspectiveView, Event, Object> perspectiveView = ((PerspectiveView, Event, Object>) perspective);

            handleUIPerspective(perspective, perspectiveView, context);

            perspective.postInit(new ComponentHandlerImpl(this.launcher, perspectiveView.getIPerspectiveLayout(), perspective
                    .getComponentDelegateQueue()));
        }
    }

    private void handleUIPerspective(final Perspective, Event, Object> perspective,
                                     final PerspectiveView, Event, Object> perspectiveView,
                                     final JacpContextImpl context) {
        final AFXPerspective perspectiveImpl = AFXPerspective.class.cast(perspective);
        if (perspectiveView.getType().equals(UIType.DECLARATIVE)) {
            handleDeclarativePerspective(perspectiveImpl, perspectiveView);
        } else {
            handleDefaultPerspective(perspectiveImpl);
        }
        FXUtil.invokeHandleMethodsByAnnotation(PostConstruct.class,
                perspective.getPerspective(), perspectiveImpl.getIPerspectiveLayout(), context.getComponentLayout(), perspective.getContext().getResourceBundle());
    }

    private void initFXComponentLayout(final JacpContextImpl context) {
        context.setFXComponentLayout(new FXComponentLayout(this.getWorkbenchLayout(), null, context.getId()));
    }

    private void handleDefaultPerspective(final AFXPerspective perspective) {
        // init default PerspectiveLayout
        initLocalization(null, perspective);
        perspective.setIPerspectiveLayout(new FXPerspectiveLayout());
    }

    private void handleDeclarativePerspective(final AFXPerspective perspective,
                                              final PerspectiveView, Event, Object> perspectiveView) {
        // init PerspectiveLayout for FXML
        final URL url = getClass().getResource(perspectiveView.getViewLocation());
        initLocalization(url, perspective);
        try {
            perspective.setIPerspectiveLayout(new FXMLPerspectiveLayout(
                    FXUtil.loadFXMLandSetController(perspectiveView.getPerspective(), perspectiveView.getContext().getResourceBundle(), url)));
        } catch (IllegalStateException e) {
            throw new MissingResourceException(
                    "fxml file not found --  place in resource folder and reference like this: viewLocation = \"/myUIFile.fxml\"",
                    url.getPath(), "");
        }

    }

    private void initLocalization(final URL url, final AFXPerspective perspective) {
        final String bundleLocation = perspective.getResourceBundleLocation();
        if (bundleLocation.isEmpty()) return;
        final String localeID = perspective.getLocaleID();
        perspective.initialize(url, ResourceBundle.getBundle(bundleLocation, FXUtil.getCorrectLocale(localeID)));

    }

    /**
     * get perspective ui root container
     *
     * @param layout, The perspective layout
     * @return the root Node
     */
    private Node getLayoutComponentFromPerspectiveLayout(final PerspectiveLayoutInterface layout) {
        return layout.getRootComponent();
    }

    /**
     * set all child component to invisible
     *
     * @param children, The list of children which should be set invisible
     */
    private void hideChildren(final ObservableList children) {
        children.forEach(c -> {
            if (c.isVisible()) c.setVisible(false);
        });
    }

    /**
     * set all child component to invisible
     *
     * @param perspective,         the current perspective
     * @param previousPerspective, the previous visible perspective
     * @param children,            JavaFX node children
     */
    private void hideChildrenAndExecuteOnHide(final Perspective, Event, Object> perspective, final Perspective, Event, Object> previousPerspective, final ObservableList children) {
        hideChildren(children);
        if (previousPerspective != null && !previousPerspective.equals(perspective)) {
            final PerspectiveView, Event, Object> perspectiveView = ((PerspectiveView, Event, Object>) previousPerspective);
            final FXComponentLayout layout = new FXComponentLayout(this.getWorkbenchLayout(), null, previousPerspective.getContext().getId());
            FXUtil.invokeHandleMethodsByAnnotation(OnHide.class, previousPerspective.getPerspective(), layout,
                    perspectiveView.getType().equals(UIType.DECLARATIVE) ? perspectiveView.getDocumentURL() : null, perspectiveView.getContext().getResourceBundle());

        }

    }

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

    final FXWorkbenchLayout getWorkbenchLayout() {
        return (FXWorkbenchLayout) this.workbenchLayout;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy