/*******************************************************************************
* Copyright (c) 2016 vogella GmbH 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: Simon Scholz <simon.scholz@vogella.com> - initial API and
* implementation
******************************************************************************/
package org.eclipse.e4.ui.internal.workbench;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.di.extensions.EventTopic;
import org.eclipse.e4.ui.di.UIEventTopic;
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.MPerspectiveStack;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
import org.eclipse.e4.ui.model.application.ui.basic.MStackElement;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.workbench.IWorkbench;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.UIEvents.EventTags;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.osgi.service.event.Event;
/**
* The {@link PartOnTopManager} is used to calculate the parts, which are on
* top. This means all parts that can be directly seen by a user in the UI.
*/
@SuppressWarnings("restriction")
public class PartOnTopManager {
@Inject
private EModelService modelService;
/**
* This event listener sets the initial onTop state, when a widget for an
* {@link MUIElement} is created.
*
* @param event
* {@link Event} containing the data of the widgets that have
* been created for a {@link MUIElement}.
*/
@Inject
@Optional
public void subscribeTopicWidget(@UIEventTopic(UIEvents.UIElement.TOPIC_WIDGET) Event event) {
Object element = event.getProperty(EventTags.ELEMENT);
Object newValue = event.getProperty(EventTags.NEW_VALUE);
if (element instanceof MPart && newValue != null) {
handlePartSelection(null, element);
}
}
/**
* This event listener updates the on top status of {@link MStackElement}
* instances by adding or removing the {@link IWorkbench#ON_TOP} tag
* according to the state.
*
* @param event
* {@link Event} containing the data of the selection change
* event on element container, like {@link MPerspectiveStack} and
* {@link MPartStack}.
*/
@Inject
@Optional
public void subscribeTopicSelectedElement(
@EventTopic(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT) Event event) {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
Object oldValue = event.getProperty(UIEvents.EventTags.OLD_VALUE);
Object newValue = event.getProperty(UIEvents.EventTags.NEW_VALUE);
if (element instanceof MPerspectiveStack) {
// stack element's contents can be hidden or shown due to a
// perspective switch ...
handlePerspectiveSwitch(oldValue, newValue);
} else if (element instanceof MPartStack) {
// ... or by changing the selection in a part stack
handlePartSelection(oldValue, newValue);
}
}
/**
* @param oldValue
* @param newValue
*/
private void handlePerspectiveSwitch(Object oldValue, Object newValue) {
handleNewAndOldSelectedElementsOfElementContainer(oldValue, newValue);
}
private void handlePartSelection(Object oldValue, Object newValue) {
Object oldElement = oldValue;
if (oldElement instanceof MPlaceholder) {
oldElement = ((MPlaceholder) oldElement).getRef();
}
if (oldElement instanceof MPart) {
MPart contextElement = (MPart) oldElement;
if (contextElement.getContext() != null) {
contextElement.getContext().set(IWorkbench.ON_TOP, Boolean.FALSE);
}
}
Object newElement = newValue;
if (newElement instanceof MPlaceholder) {
newElement = ((MPlaceholder) newElement).getRef();
}
if (newElement instanceof MPart) {
MPart contextElement = (MPart) newElement;
if (contextElement.getContext() != null) {
contextElement.getContext().set(IWorkbench.ON_TOP, Boolean.TRUE);
}
}
handleNewAndOldSelectedElementsOfElementContainer(oldElement, newElement);
}
private void handleNewAndOldSelectedElementsOfElementContainer(Object oldValue, Object newValue) {
if (newValue instanceof MElementContainer) {
List<MPart> newParts = modelService.findElements((MElementContainer<?>) newValue, null, MPart.class, null);
if (oldValue instanceof MElementContainer) {
List<MPart> oldParts = modelService.findElements((MElementContainer<?>) oldValue, null, MPart.class,
null);
oldParts.forEach(part -> {
if (part.getContext() != null) {
if (!isPartOnTop(newParts, part)) {
part.getContext().set(IWorkbench.ON_TOP, Boolean.FALSE);
}
}
});
}
handleNewSelectedElementsOfElementContainer(newParts);
}
}
private boolean isPartOnTop(List<MPart> newParts, MPart part) {
if (part.getCurSharedRef() != null) {
if (newParts.contains(part)) {
java.util.Optional<MPartStack> partStackContainer = getPartStackContainer(part.getCurSharedRef());
if (partStackContainer.isPresent()) {
return part.getCurSharedRef().equals(partStackContainer.get().getSelectedElement());
}
}
}
return false;
}
private void handleNewSelectedElementsOfElementContainer(List<MPart> parts) {
parts.stream().filter(part -> {
if (part.getContext() == null) {
return false;
}
MUIElement partRef = part;
if (part.getCurSharedRef() != null) {
partRef = part.getCurSharedRef();
}
java.util.Optional<MPartStack> partStackContainer = getPartStackContainer(partRef);
if (partStackContainer.isPresent()) {
return partRef.equals(partStackContainer.get().getSelectedElement());
}
return true;
}).forEach(part -> {
part.getContext().set(IWorkbench.ON_TOP, Boolean.TRUE);
});
}
private java.util.Optional<MPartStack> getPartStackContainer(MUIElement uiElement) {
MUIElement parent = modelService.getContainer(uiElement);
if (parent instanceof MPartStack) {
return java.util.Optional.of((MPartStack) parent);
} else if (parent instanceof MWindow) {
// reached window level so there is no MPartStack as parent
return java.util.Optional.empty();
}
return getPartStackContainer(parent);
}
}