blob: a752bfd789d4a965e8002e6c945eb004261ede54 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2018 IBM Corporation and others.
* 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:
* Eugen Neufeld (eneufeld@eclipsesource.com) - initial API and implementation
******************************************************************************/
package org.eclipse.e4.ui.workbench.addons.minmax;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.advanced.MArea;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainer;
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainerElement;
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
/**
* An utility class which provides methods to handle the cases for the special
* area.
*/
// MinMaximizeableChildrenArea
public class MinMaxAddonUtil {
private static final String ID_EDITOR_AREA = "org.eclipse.ui.editorss"; //$NON-NLS-1$
private static String MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG = IPresentationEngine.MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG;
/**
* Check whether the passed {@link MUIElement} is an MArea with a
* MinMaximizeableChildrenArea tag and multiple children that are visible
* and not minimized.
*
* @param element
* The element to check.
* @return true if the element is a MArea with an
* MinMaximizeableChildrenArea tag and multiple visible not
* minimized children, false otherwise
*/
public static boolean isMinMaxChildrenAreaWithMultipleVisibleChildren(MUIElement element) {
if (!element.getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
return false;
}
if (!(element instanceof MArea)) {
return false;
}
MArea area = (MArea) element;
if (area.getChildren().isEmpty()) {
return false;
}
if (!hasMoreThenOneVisibleRenderableChild(area.getChildren().get(0))) {
return false;
}
return true;
}
private static boolean hasMoreThenOneVisibleRenderableChild(MPartSashContainerElement elementToCheck) {
if (elementToCheck instanceof MPartSashContainer) {
int partsToRender = 0;
for (MPartSashContainerElement part : ((MPartSashContainer) elementToCheck).getChildren()) {
boolean hasMinimizeableChild = hasMoreThenOneVisibleRenderableChild(part);
if (hasMinimizeableChild) {
return true;
}
if (isVisible(part)) {
partsToRender++;
}
}
if (partsToRender > 1) {
return true;
}
}
return false;
}
private static boolean isVisible(MUIElement part) {
boolean visible = part.isToBeRendered() && part.isVisible();
if (part instanceof MElementContainer && visible) {
visible = false;
for (Object element : ((MElementContainer<?>) part).getChildren()) {
MUIElement innerElement = (MUIElement) element;
visible |= isVisible(innerElement);
}
}
return visible;
}
/**
* Remove all partstacks that are inside a special area if the element that
* is maximized is a special area.
*
* @param modelService
* The {@link EModelService} to use
* @param element
* The {@link MUIElement} getting maximized
* @param curMax
* The list of elements to restore
*/
// TODO needed, doesn't this exclude itself with getElementsToRestore?
public static void ignoreChildrenOfMinMaxChildrenArea(EModelService modelService, MUIElement element,
List<MUIElement> curMax) {
if (element instanceof MPlaceholder
&& ((MPlaceholder) element).getRef().getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
Set<MUIElement> toRemove = new LinkedHashSet<>();
for (MUIElement maxElement : curMax) {
if (modelService.find(maxElement.getElementId(), element) != null) {
toRemove.add(maxElement);
}
}
curMax.removeAll(toRemove);
}
}
/**
* Check for {@link MPartStack MPartStacks} that are inside a special area
* and exclude them from being restored as they have to be handled
* specially.
*
* @param modelService
* The {@link EModelService} to use
* @param element
* The {@link MUIElement} being restored
* @param win
* The {@link MWindow} the {@link MUIElement} is part of
* @param persp
* The {@link MPerspective} the {@link MUIElement} is part of
* @param elementsToRestore
* The list of elements to restore
*/
public static void addChildrenOfMinMaxChildrenAreaToRestoreList(EModelService modelService, MUIElement element,
MWindow win, MPerspective persp, List<MUIElement> elementsToRestore) {
List<MPlaceholder> areas = modelService.findElements(persp == null ? win : persp, ID_EDITOR_AREA,
MPlaceholder.class, null, EModelService.PRESENTATION);
for (MPlaceholder placeholder : areas) {
if (placeholder == element) {
continue;
}
if (win != getWindowFor(placeholder)) {
continue;
}
if (!placeholder.getRef().getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
continue;
}
List<MPartStack> partStacks = modelService.findElements(placeholder, null, MPartStack.class,
null);
if (partStacks.contains(element)) {
continue;
}
for (MPartStack partStack : partStacks) {
elementsToRestore.remove(partStack);
}
}
}
/**
* Restore the inner stack of an area and adjust the buttons of the restored
* stack.
*
* @param minMaxAddon
* The MinMaxAddon calling this
* @param element
* The MUIElement being restored.
* @param maximizeTag
* The List of tags to search for
*/
public static void restoreStacksOfMinMaxChildrenArea(final MinMaxAddon minMaxAddon, MUIElement element,
List<String> maximizeTag) {
if (element instanceof MPartStack) {
MArea area = getAreaFor((MPartStack) element);
if (area != null && area.getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
final List<MPartStack> maximizedAreaChildren = minMaxAddon.modelService.findElements(area, null,
MPartStack.class,
maximizeTag);
minMaxAddon.executeWithIgnoredTagChanges(() -> {
for (MPartStack partStack : maximizedAreaChildren) {
partStack.getTags().remove(IPresentationEngine.MAXIMIZED);
minMaxAddon.adjustCTFButtons(partStack);
}
});
}
}
}
/**
* Maximize the area if the inner part stack is maximized and adjust the
* buttons of the maximized area.
*
* @param minMaxAddon
* The MinMaxAddon calling this
* @param element
* The MUIElement being restored.
*/
public static void maximizeMinMaxChildrenArea(final MinMaxAddon minMaxAddon, final MUIElement element) {
if (element instanceof MPartStack) {
MArea area = getAreaFor((MPartStack) element);
if (area != null && area.getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
final MPlaceholder placeholder = area.getCurSharedRef();
minMaxAddon
.executeWithIgnoredTagChanges(() -> placeholder.getTags().add(IPresentationEngine.MAXIMIZED));
minMaxAddon.adjustCTFButtons(placeholder);
}
}
}
/**
* Check whether a special table is one of the minimized elements. If so do
* not minimized any contents of such a table.
*
* @param modelService
* The {@link EModelService} to use
* @param element
* The {@link MUIElement} being minimized
* @param win
* The {@link MWindow} the minimized element is part of
* @param persp
* The {@link MPerspective} the minimized element is part of
* @param elementsToMinimize
* The list of elements to minimize
*/
public static void handleMinimizeOfMinMaxChildrenArea(EModelService modelService, MUIElement element, MWindow win,
MPerspective persp, List<MUIElement> elementsToMinimize) {
// if an area is minimized, exclude the children from being minimized
List<MPlaceholder> areas = modelService.findElements(persp == null ? win : persp, ID_EDITOR_AREA,
MPlaceholder.class, null, EModelService.ANYWHERE);
boolean foundRelevantArea = false;
for (MPlaceholder placeholder : areas) {
if (placeholder == element) {
continue;
}
if (win != getWindowFor(placeholder)) {
continue;
}
if (modelService.find(element.getElementId(), placeholder) == null) {
continue;
}
if (placeholder.getRef().getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
foundRelevantArea = true;
}
List<MPartStack> partStacks = modelService.findElements(placeholder, null, MPartStack.class,
null);
for (MPartStack partStack : partStacks) {
if (partStack == element) {
continue;
}
elementsToMinimize.add(partStack);
}
}
if (foundRelevantArea) {
List<MUIElement> elementsToRemove = new ArrayList<>();
for (MUIElement elementMUI : elementsToMinimize) {
List<Object> findElements = modelService.findElements(elementMUI, element.getElementId(),
null, null);
if (findElements != null && findElements.size() != 0) {
elementsToRemove.add(elementMUI);
}
}
elementsToMinimize.removeAll(elementsToRemove);
}
// allow Area Children to be maximizable
}
/**
* Unzoom the special area if a child of it is unzoomed.
*
* @param minMaxAddon
* The {@link MinMaxAddon} calling
* @param element
* The {@link MUIElement} being unzoomed
*/
public static void unzoomStackOfMinMaxChildrenArea(final MinMaxAddon minMaxAddon, final MUIElement element) {
if (element instanceof MPartStack) {
MArea area = getAreaFor((MPartStack) element);
if (area != null && area.getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
final MPlaceholder placeholder = area.getCurSharedRef();
minMaxAddon.executeWithIgnoredTagChanges(
() -> placeholder.getTags().remove(IPresentationEngine.MAXIMIZED));
minMaxAddon.adjustCTFButtons(placeholder);
}
}
}
/**
* Check whether a {@link MUIElement} is a child of a special area.
*
* @param element
* The {@link MUIElement} to check
* @return true if the element is part of a special area, false otherwise
*/
public static boolean isPartOfMinMaxChildrenArea(MUIElement element) {
if (element instanceof MPartStack) {
MArea area = getAreaFor((MPartStack) element);
if (area != null && area.getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG)) {
return true;
}
}
return false;
}
/**
* Return the first {@link MArea} that is the parent of the given
* {@link MPartStack}.
*
* @param stack
* The {@link MPartStack} to find the container {@link MArea} for
* @return The container MArea or null if this stack is not part of an area
*/
public static MArea getAreaFor(MPartStack stack) {
MUIElement parent = stack.getParent();
while (parent != null) {
if (parent instanceof MArea) {
return (MArea) parent;
}
parent = parent.getParent();
}
return null;
}
/**
* Return the MWindow containing this element (if any). This may either be a
* 'top level' window -or- a detached window. This allows the min.max code
* to only affect elements in the window containing the element.
*
* @param element
* The element to check
*
* @return the window containing the element.
*/
public static MWindow getWindowFor(MUIElement element) {
MUIElement parent = element.getParent();
// We rely here on the fact that a DW's 'getParent' will return
// null since it's not in the 'children' hierarchy
while (parent != null && !(parent instanceof MWindow)) {
if (parent.getTags().contains(MIN_MAXIMIZEABLE_CHILDREN_AREA_TAG) && parent instanceof MArea) {
parent = ((MArea) parent).getCurSharedRef();
} else {
parent = parent.getParent();
}
}
// A detached window will end up with getParent() == null
return (MWindow) parent;
}
}