/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.workspace.perspectives.general; import elemental.json.Json; import elemental.json.JsonArray; import elemental.json.JsonObject; import com.google.inject.Provider; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.ide.api.constraints.Constraints; import org.eclipse.che.ide.api.event.ActivePartChangedEvent; import org.eclipse.che.ide.api.event.ActivePartChangedHandler; import org.eclipse.che.ide.api.mvp.Presenter; import org.eclipse.che.ide.api.parts.PartPresenter; import org.eclipse.che.ide.api.parts.PartStack; import org.eclipse.che.ide.api.parts.PartStackType; import org.eclipse.che.ide.api.parts.PartStackView; import org.eclipse.che.ide.api.parts.Perspective; import org.eclipse.che.ide.api.parts.PerspectiveView; import org.eclipse.che.ide.api.parts.base.MaximizePartEvent; import org.eclipse.che.ide.workspace.PartStackPresenterFactory; import org.eclipse.che.ide.workspace.PartStackViewFactory; import org.eclipse.che.ide.workspace.WorkBenchControllerFactory; import org.eclipse.che.ide.workspace.WorkBenchPartController; import org.eclipse.che.providers.DynaProvider; import javax.validation.constraints.NotNull; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.eclipse.che.ide.api.parts.PartStackType.EDITING; import static org.eclipse.che.ide.api.parts.PartStackType.INFORMATION; import static org.eclipse.che.ide.api.parts.PartStackType.NAVIGATION; import static org.eclipse.che.ide.api.parts.PartStackType.TOOLING; import static org.eclipse.che.ide.api.parts.PartStackView.TabPosition.BELOW; import static org.eclipse.che.ide.api.parts.PartStackView.TabPosition.LEFT; import static org.eclipse.che.ide.api.parts.PartStackView.TabPosition.RIGHT; /** * The class which contains general business logic for all perspectives. * * @author Dmitry Shnurenko */ //TODO need rewrite this, remove direct dependency on PerspectiveViewImpl and other GWT Widgets public abstract class AbstractPerspective implements Presenter, Perspective, ActivePartChangedHandler, MaximizePartEvent.Handler, PerspectiveView.ActionDelegate, PartStack.ActionDelegate { /** The default size for the part. */ private static final double DEFAULT_PART_SIZE = 260; /** The minimum allowable size for the part. */ private static final int MIN_PART_SIZE = 100; protected final Map<PartStackType, PartStack> partStacks; protected final PerspectiveViewImpl view; private final String perspectiveId; private final DynaProvider dynaProvider; private final WorkBenchPartController leftPartController; private final WorkBenchPartController rightPartController; private final WorkBenchPartController belowPartController; private PartPresenter activePart; private PartPresenter activePartBeforeChangePerspective; private PartStack maximizedPartStack; protected AbstractPerspective(@NotNull String perspectiveId, @NotNull PerspectiveViewImpl view, @NotNull PartStackPresenterFactory stackPresenterFactory, @NotNull PartStackViewFactory partViewFactory, @NotNull WorkBenchControllerFactory controllerFactory, @NotNull EventBus eventBus, @NotNull DynaProvider dynaProvider) { this.view = view; this.perspectiveId = perspectiveId; this.dynaProvider = dynaProvider; this.partStacks = new HashMap<>(); view.setDelegate(this); PartStackView navigationView = partViewFactory.create(LEFT, view.getLeftPanel()); leftPartController = controllerFactory.createController(view.getSplitPanel(), view.getNavigationPanel()); PartStack navigationPartStack = stackPresenterFactory.create(navigationView, leftPartController); navigationPartStack.setDelegate(this); partStacks.put(NAVIGATION, navigationPartStack); PartStackView informationView = partViewFactory.create(BELOW, view.getBottomPanel()); belowPartController = controllerFactory.createController(view.getSplitPanel(), view.getInformationPanel()); PartStack informationStack = stackPresenterFactory.create(informationView, belowPartController); informationStack.setDelegate(this); partStacks.put(INFORMATION, informationStack); PartStackView toolingView = partViewFactory.create(RIGHT, view.getRightPanel()); rightPartController = controllerFactory.createController(view.getSplitPanel(), view.getToolPanel()); PartStack toolingPartStack = stackPresenterFactory.create(toolingView, rightPartController); toolingPartStack.setDelegate(this); partStacks.put(TOOLING, toolingPartStack); eventBus.addHandler(ActivePartChangedEvent.TYPE, this); eventBus.addHandler(MaximizePartEvent.TYPE, this); } /** * Opens previous active tab on current perspective. * * @param partStackType * part type on which need open previous active part */ protected void openActivePart(@NotNull PartStackType partStackType) { PartStack partStack = partStacks.get(partStackType); partStack.openPreviousActivePart(); } @Override public void storeState() { activePartBeforeChangePerspective = activePart; if (activePartBeforeChangePerspective != null) { activePartBeforeChangePerspective.storeState(); } } @Override public void restoreState() { if (activePartBeforeChangePerspective != null) { setActivePart(activePartBeforeChangePerspective); activePartBeforeChangePerspective.restoreState(); } } @Override public void onActivePartChanged(ActivePartChangedEvent event) { activePart = event.getActivePart(); } /** {@inheritDoc} */ @Override public void removePart(@NotNull PartPresenter part) { PartStack destPartStack = findPartStackByPart(part); if (destPartStack != null) { destPartStack.removePart(part); } } /** {@inheritDoc} */ @Override public void hidePart(@NotNull PartPresenter part) { PartStack destPartStack = findPartStackByPart(part); if (destPartStack != null) { destPartStack.minimize(); } } /** {@inheritDoc} */ @Override public void maximizeCentralPartStack() { onMaximize(partStacks.get(EDITING)); } /** {@inheritDoc} */ @Override public void maximizeLeftPartStack() { onMaximize(partStacks.get(NAVIGATION)); } /** {@inheritDoc} */ @Override public void maximizeRightPartStack() { onMaximize(partStacks.get(TOOLING)); } /** {@inheritDoc} */ @Override public void maximizeBottomPartStack() { onMaximize(partStacks.get(INFORMATION)); } @Override public void onMaximizePart(MaximizePartEvent event) { PartStack partStack = findPartStackByPart(event.getPart()); if (partStack == null) { return; } if (partStack.getPartStackState() == PartStack.State.MAXIMIZED) { onRestore(partStack); } else { onMaximize(partStack); } } @Override public void onMaximize(PartStack partStack) { if (partStack == null) { return; } if (partStack.equals(maximizedPartStack)) { return; } maximizedPartStack = partStack; for (PartStack ps : partStacks.values()) { if (!ps.equals(partStack)) { ps.collapse(); } } partStack.maximize(); } @Override public void onRestore(PartStack partStack) { for (PartStack ps : partStacks.values()) { ps.restore(); } maximizedPartStack = null; } /** {@inheritDoc} */ @Override public void restore() { onRestore(null); } /** {@inheritDoc} */ @Override public void setActivePart(@NotNull PartPresenter part) { PartStack destPartStack = findPartStackByPart(part); if (destPartStack != null) { destPartStack.setActivePart(part); } } /** {@inheritDoc} */ @Override public void setActivePart(@NotNull PartPresenter part, @NotNull PartStackType type) { PartStack destPartStack = partStacks.get(type); destPartStack.setActivePart(part); } /** * Find parent PartStack for given Part * * @param part * part for which need find parent * @return Parent PartStackPresenter or null if part not registered */ public PartStack findPartStackByPart(@NotNull PartPresenter part) { for (PartStackType partStackType : PartStackType.values()) { if (partStacks.get(partStackType).containsPart(part)) { return partStacks.get(partStackType); } } return null; } /** {@inheritDoc} */ @Override public void addPart(@NotNull PartPresenter part, @NotNull PartStackType type) { addPart(part, type, null); } /** {@inheritDoc} */ @Override public void addPart(@NotNull PartPresenter part, @NotNull PartStackType type, @Nullable Constraints constraint) { PartStack destPartStack = partStacks.get(type); List<String> rules = part.getRules(); if (rules.isEmpty() || rules.contains(perspectiveId)) { destPartStack.addPart(part, constraint); return; } } @Override public void onResize(int width, int height) { if (maximizedPartStack != null) { maximizedPartStack.maximize(); } } /** {@inheritDoc} */ @Override @Nullable public PartStack getPartStack(@NotNull PartStackType type) { return partStacks.get(type); } @Override public JsonObject getState() { JsonObject state = Json.createObject(); JsonObject partStacks = Json.createObject(); state.put("ACTIVE_PART", activePart.getClass().getName()); state.put("PART_STACKS", partStacks); partStacks.put(PartStackType.INFORMATION.name(), getPartStackState(this.partStacks.get(INFORMATION), belowPartController)); partStacks.put(PartStackType.NAVIGATION.name(), getPartStackState(this.partStacks.get(NAVIGATION), leftPartController)); partStacks.put(PartStackType.TOOLING.name(), getPartStackState(this.partStacks.get(TOOLING), rightPartController)); return state; } private JsonObject getPartStackState(PartStack partStack, WorkBenchPartController partController) { JsonObject state = Json.createObject(); state.put("SIZE", partController.getSize()); state.put("STATE", partStack.getPartStackState().name()); if (partStack.getParts().isEmpty()) { state.put("HIDDEN", true); } else { if (partStack.getActivePart() != null) { state.put("ACTIVE_PART", partStack.getActivePart().getClass().getName()); } state.put("HIDDEN", partController.isHidden()); JsonArray parts = Json.createArray(); state.put("PARTS", parts); int i = 0; for (PartPresenter entry : partStack.getParts()) { JsonObject presenterState = Json.createObject(); presenterState.put("CLASS", entry.getClass().getName()); parts.set(i++, presenterState); } } return state; } @Override public void loadState(@NotNull JsonObject state) { if (state.hasKey("PART_STACKS")) { JsonObject partStacksState = state.getObject("PART_STACKS"); // Don't restore part dimensions if perspective is maximized. boolean perspectiveMaximized = isPerspectiveMaximized(partStacksState); for (String partStackType : partStacksState.keys()) { JsonObject partStackState = partStacksState.getObject(partStackType); switch (PartStackType.valueOf(partStackType)) { case INFORMATION: loadPartStackState(partStacks.get(INFORMATION), belowPartController, partStackState, perspectiveMaximized); break; case NAVIGATION: loadPartStackState(partStacks.get(NAVIGATION), leftPartController, partStackState, perspectiveMaximized); break; case TOOLING: loadPartStackState(partStacks.get(TOOLING), rightPartController, partStackState, perspectiveMaximized); break; } } } // restore perspective's active part if (state.hasKey("ACTIVE_PART")) { String activePart = state.getString("ACTIVE_PART"); Provider<PartPresenter> provider = dynaProvider.getProvider(activePart); if (provider != null) { setActivePart(provider.get()); } } } /** * Determines whether perspective is maximized. * * @param partStacksState part stack state * @return <b>true</b> is perspective has maximized part stack */ private boolean isPerspectiveMaximized(JsonObject partStacksState) { for (String partStackType : partStacksState.keys()) { JsonObject partStackState = partStacksState.getObject(partStackType); if (partStackState.hasKey("STATE") && PartStack.State.MAXIMIZED.name().equals(partStackState.getString("STATE"))) { return true; } } return false; } /** * Set part stack state. * * @param partStack * @param controller * @param partStackState * @param skipRestoreDimensions */ private void loadPartStackState(PartStack partStack, WorkBenchPartController controller, JsonObject partStackState, boolean skipRestoreDimensions) { if (partStackState.hasKey("PARTS")) { JsonArray parts = partStackState.get("PARTS"); for (int i = 0; i < parts.length(); i++) { JsonObject value = parts.get(i); if (value.hasKey("CLASS")) { String className = value.getString("CLASS"); Provider<PartPresenter> provider = dynaProvider.getProvider(className); if (provider != null) { PartPresenter partPresenter = provider.get(); if (!partStack.containsPart(partPresenter)) { partStack.addPart(partPresenter); } } } } } // restore part stack's active part if (partStackState.hasKey("ACTIVE_PART")) { String activePart = partStackState.getString("ACTIVE_PART"); Provider<PartPresenter> provider = dynaProvider.getProvider(activePart); if (provider != null) { partStack.setActivePart(provider.get()); } } //hide part stack if it has no parts if (partStack.getParts().isEmpty()) { controller.setHidden(true); return; } if (skipRestoreDimensions) { return; } if (partStackState.hasKey("HIDDEN") && partStackState.getBoolean("HIDDEN")) { partStack.minimize(); return; } if (partStackState.hasKey("SIZE")) { double size = partStackState.getNumber("SIZE"); // Size of the part must not be less 100 pixels. if (size <= MIN_PART_SIZE) { size = DEFAULT_PART_SIZE; } controller.setSize(size); } } }