blob: 2151df0591ad84b81fdf626e11e7459f09ba2f9d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 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:
* IBM Corporation - initial API and implementation
******************************************************************************/
package org.eclipse.ui.internal.e4.migration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
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.SideValue;
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.MStackElement;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchConstants;
import org.eclipse.ui.internal.PerspectiveTagger;
import org.eclipse.ui.internal.e4.compatibility.ModeledPageLayout;
import org.eclipse.ui.internal.e4.compatibility.ModeledPageLayoutUtils;
import org.eclipse.ui.internal.e4.migration.InfoReader.PageReader;
import org.eclipse.ui.internal.e4.migration.InfoReader.PartState;
import org.eclipse.ui.internal.e4.migration.PerspectiveReader.DetachedWindowReader;
import org.eclipse.ui.internal.e4.migration.PerspectiveReader.ViewLayoutReader;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.registry.RegistryReader;
import org.eclipse.ui.internal.registry.StickyViewDescriptor;
public class PerspectiveBuilder {
static final String ORIGINAL_ID = "originalId"; //$NON-NLS-1$
static final String BASE_PERSPECTIVE_ID = "basePerspectiveId"; //$NON-NLS-1$
private static final String DEFAULT_FASTVIEW_STACK = "defaultFastViewStack"; //$NON-NLS-1$
private static final String ID_EDITOR_AREA = IPageLayout.ID_EDITOR_AREA;
@Inject
private PerspectiveReader perspReader;
@Inject
private EModelService modelService;
private MPerspective perspective;
private List<String> tags;
private List<String> renderedViews;
private List<String> defaultFastViews;
private Map<String, MPlaceholder> viewPlaceholders = new HashMap<>();
private Map<String, ViewLayoutReader> viewLayouts;
private MPlaceholder editorAreaPlaceholder;
private ModeledPageLayoutUtils layoutUtils;
private Integer defaultFastViewSide;
@PostConstruct
private void postConstruct() {
layoutUtils = new ModeledPageLayoutUtils(modelService);
}
public MPerspective createPerspective() {
create();
tags = perspective.getTags();
populate();
return perspective;
}
public MPerspective createPerspective(Integer defaultFastViewSide) {
this.defaultFastViewSide = defaultFastViewSide;
return createPerspective();
}
private void create() {
perspective = modelService.createModelElement(MPerspective.class);
perspective.setElementId(perspReader.getId());
String label = perspReader.getLabel();
perspective.setLabel(label);
perspective.setTooltip(label);
if (perspReader.isCustom()) {
perspective.getTransientData().put(BASE_PERSPECTIVE_ID, perspReader.getBasicPerspectiveId());
perspective.getTransientData().put(ORIGINAL_ID, perspReader.getOriginalId());
}
}
private void populate() {
addActionSetTags();
addPerspectiveShortcutTags();
addNewWizardTags();
addShowViewTags();
addHiddenItems();
addShowInTags();
for (InfoReader info : perspReader.getInfos()) {
if (info.isEditorArea()) {
addEditorArea(info);
} else if (info.isFolder()) {
MPartStack stack = addPartStack(info);
populatePartStack(stack, info);
}
}
addDefaultFastViewStack();
setZoomState();
addDetachedWindows();
hideEmptyStacks();
hideUrenderableSashes();
hideInvisibleSashes();
processStandaloneViews();
correctSelectedElements();
addTrimBars();
PerspectiveTagger.tagPerspective(perspective, modelService);
}
private void processStandaloneViews() {
Map<String, ViewLayoutReader> viewLayouts = perspReader.getViewLayouts();
for (Entry<String, ViewLayoutReader> entry : viewLayouts.entrySet()) {
String viewId = entry.getKey();
MPlaceholder placeholder = viewPlaceholders.get(viewId);
if (placeholder == null) {
continue;
}
if (entry.getValue().isStandalone()) {
MElementContainer<MUIElement> parent = placeholder.getParent();
placeholder.setContainerData(parent.getContainerData());
parent.getChildren().remove(placeholder);
MElementContainer<MUIElement> grandParent = parent.getParent();
int location = grandParent.getChildren().indexOf(parent);
grandParent.getChildren().add(location, placeholder);
grandParent.getChildren().remove(parent);
}
}
}
private void addDetachedWindows() {
for (DetachedWindowReader detachedWindowReader : perspReader.getDetachedWindows()) {
MTrimmedWindow detachedWindow = modelService.createModelElement(MTrimmedWindow.class);
Rectangle bounds = detachedWindowReader.getBounds();
detachedWindow.setX(bounds.x);
detachedWindow.setY(bounds.y);
detachedWindow.setWidth(bounds.width);
detachedWindow.setHeight(bounds.height);
MPartStack stack = modelService.createModelElement(MPartStack.class);
populatePartStack(stack, detachedWindowReader);
detachedWindow.getChildren().add(stack);
perspective.getWindows().add(detachedWindow);
}
}
private void addTrimBars() {
Map<String, Integer> fastViewBars = perspReader.getFastViewBars();
if (fastViewBars.size() == 0 && defaultFastViews.size() == 0) {
return;
}
int topCounter = 0;
int bottomCounter = 0;
int rightCounter = 0;
int leftCounter = 0;
StringBuilder sb = new StringBuilder();
if (defaultFastViews.size() > 0) {
sb.append(DEFAULT_FASTVIEW_STACK).append(' ');
if (defaultFastViewSide != null) {
switch (defaultFastViewSide) {
case SWT.TOP:
sb.append(SideValue.TOP_VALUE).append(' ').append(topCounter++);
break;
case SWT.BOTTOM:
sb.append(SideValue.BOTTOM_VALUE).append(' ').append(bottomCounter++);
break;
case SWT.RIGHT:
sb.append(SideValue.RIGHT_VALUE).append(' ').append(rightCounter++);
break;
default:
sb.append(SideValue.LEFT_VALUE).append(' ').append(leftCounter++);
break;
}
} else {
sb.append(SideValue.BOTTOM_VALUE).append(' ').append(bottomCounter++);
}
sb.append('#');
}
if (fastViewBars.size() > 0) {
for (InfoReader folder : perspReader.getInfos()) {
String folderId = folder.getId();
if (!fastViewBars.containsKey(folderId)) {
continue;
}
sb.append(folderId).append(' ');
Integer side = fastViewBars.get(folderId);
if (side == null) {
side = SWT.LEFT;
}
switch (side) {
case SWT.TOP:
sb.append(SideValue.TOP_VALUE).append(' ').append(topCounter++);
break;
case SWT.BOTTOM:
sb.append(SideValue.BOTTOM_VALUE).append(' ').append(bottomCounter++);
break;
case SWT.RIGHT:
sb.append(SideValue.RIGHT_VALUE).append(' ').append(rightCounter++);
break;
default:
sb.append(SideValue.LEFT_VALUE).append(' ').append(leftCounter++);
break;
}
sb.append('#');
}
}
perspective.getPersistedState().put("trims", sb.toString()); //$NON-NLS-1$
}
private void hideEmptyStacks() {
for (MPartStack stack : modelService.findElements(perspective, null, MPartStack.class, null)) {
if (ID_EDITOR_AREA.equals(stack.getElementId()) || ID_EDITOR_AREA.equals(stack.getParent().getElementId())) {
continue;
}
if (!hasRenderableContent(stack)) {
stack.setToBeRendered(false);
}
}
}
private void setZoomState() {
List<MPartStack> stacks = modelService.findElements(perspective, null, MPartStack.class, null);
boolean isAnythingMaximized = isMaximized(editorAreaPlaceholder) || isAnyMaximized(stacks);
if (isAnythingMaximized) {
markMinimizedByZoom(editorAreaPlaceholder);
for (MPartStack stack : stacks) {
markMinimizedByZoom(stack);
}
}
}
private void markMinimizedByZoom(MUIElement element) {
List<String> tags = element.getTags();
if (tags.contains(IPresentationEngine.MINIMIZED)) {
tags.add(IPresentationEngine.MINIMIZED_BY_ZOOM);
}
}
private boolean isAnyMaximized(List<MPartStack> stacks) {
for (MPartStack stack : stacks) {
if (isMaximized(stack)) {
return true;
}
}
return false;
}
private boolean isMaximized(MUIElement element) {
return element.getTags().contains(IPresentationEngine.MAXIMIZED);
}
private void hideUrenderableSashes() {
for (MPartSashContainer sash : modelService.findElements(perspective, null, MPartSashContainer.class, null)) {
hideUnrenderableSash(sash);
}
}
private void hideInvisibleSashes() {
for (MPartSashContainer sash : modelService.findElements(perspective, null, MPartSashContainer.class, null)) {
hideInvisibleSash(sash);
}
}
private void hideInvisibleSash(MElementContainer<?> container) {
if ((container instanceof MPartSashContainer) && container != perspective) {
if (!hasVisibleContent((MPartSashContainer) container)) {
container.setVisible(false);
hideInvisibleSash(container.getParent());
}
}
}
private boolean hasVisibleContent(MPartSashContainer sash) {
for (MPartSashContainerElement child : sash.getChildren()) {
if (child.isVisible()) {
return true;
}
}
return false;
}
private void hideUnrenderableSash(MElementContainer<?> container) {
if ((container instanceof MPartSashContainer) && container != perspective) {
if (modelService.countRenderableChildren(container) == 0) {
container.setToBeRendered(false);
hideUnrenderableSash(container.getParent());
}
}
}
private boolean hasRenderableContent(MPartStack stack) {
for (MStackElement child : stack.getChildren()) {
if (child.isVisible() && child.isToBeRendered()) {
return true;
}
}
return false;
}
private void correctSelectedElements() {
List<MPartSashContainerElement> perspChildren = perspective.getChildren();
if (perspective.getSelectedElement() == null && !perspChildren.isEmpty()) {
for (MPartSashContainerElement child : perspChildren) {
if (child.isToBeRendered()) {
perspective.setSelectedElement(child);
break;
}
}
}
for (MPartSashContainerElement child : perspChildren) {
correctSelectedElements(child);
}
}
private void correctSelectedElements(MUIElement element) {
if (!(element instanceof MPartSashContainer || element instanceof MPartStack)) {
return;
}
@SuppressWarnings("unchecked")
MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) element;
List<MUIElement> children = container.getChildren();
if (container.getSelectedElement() == null && !children.isEmpty()) {
MUIElement firstRenderableElement = getFirstRenderableElement(children);
if (firstRenderableElement != null) {
container.setSelectedElement(firstRenderableElement);
}
}
for (MUIElement child : children) {
correctSelectedElements(child);
}
}
private MUIElement getFirstRenderableElement(List<MUIElement> elements) {
for (MUIElement element : elements) {
if (element.isToBeRendered()) {
return element;
}
}
return null;
}
private void addToPerspective(MPartSashContainerElement element, InfoReader info) {
if (info.isRelativelyPositioned()) {
insert(element, info);
} else {
perspective.getChildren().add(element);
}
}
private void addEditorArea(InfoReader info) {
editorAreaPlaceholder = modelService.createModelElement(MPlaceholder.class);
editorAreaPlaceholder.setElementId(ID_EDITOR_AREA);
editorAreaPlaceholder.setToBeRendered(perspReader.isEditorAreaVisible());
setPartState(editorAreaPlaceholder, perspReader.getEditorAreaState());
addToPerspective(editorAreaPlaceholder, info);
}
private MPartStack addPartStack(InfoReader info) {
MPartStack stack = createPartStack(info);
if (info.isRelativelyPositioned()) {
String refElementId = info.getRelative();
MUIElement refElement = modelService.find(refElementId, perspective);
MElementContainer<?> parent = refElement.getParent();
// don't put a stack in another stack
if (parent instanceof MPartStack) {
refElement = parent;
}
insert(stack, refElement, info);
} else {
perspective.getChildren().add(stack);
}
setPartState(stack, info.getState());
return stack;
}
private MPartStack addDefaultFastViewStack() {
MPartStack stack = null;
List<String> views = perspReader.getDefaultFastViewBarViewIds();
if (views.size() > 0) {
stack = layoutUtils.createStack(DEFAULT_FASTVIEW_STACK, true);
MPartSashContainer psc = modelService.createModelElement(MPartSashContainer.class);
psc.setHorizontal(true);
psc.setContainerData(Integer.toString(5000));
stack.setContainerData(Integer.toString(2500));
psc.getChildren().add(stack);
List<MPartSashContainer> list = modelService.findElements(perspective, null, MPartSashContainer.class,
null);
if (list == null || list.size() == 0) {
perspective.getChildren().add(psc);
} else {
int size = list.size();
MPartSashContainer container = list.get(size - 1);
container.getChildren().add(psc);
}
setPartState(stack, org.eclipse.ui.internal.e4.migration.InfoReader.PartState.MINIMIZED);
for (String view : views) {
addPlaceholderToDefaultFastViewStack(stack, view, null);
}
}
return stack;
}
private void setPartState(MUIElement element, PartState state) {
List<String> tags = element.getTags();
switch (state) {
case MINIMIZED:
tags.add(IPresentationEngine.MINIMIZED);
element.setVisible(false);
break;
case MAXIMIZED:
tags.add(IPresentationEngine.MAXIMIZED);
break;
default:
break;
}
}
private void insert(MUIElement element, MUIElement refElement, InfoReader info) {
layoutUtils.insert(element, refElement, layoutUtils.plRelToSwt(info.getRelationship()), info.getRatio());
}
private void insert(MUIElement element, InfoReader info) {
insert(element, modelService.find(info.getRelative(), perspective), info);
}
private MPartStack createPartStack(InfoReader info) {
String stackId = info.getId();
if (stackId != null) {
if (stackId.equals(StickyViewDescriptor.STICKY_FOLDER_RIGHT)) {
stackId = "legacyStickyFolderRight"; //$NON-NLS-1$
}
}
return layoutUtils.createStack(stackId, true);
}
private void populatePartStack(MPartStack stack, InfoReader info) {
for (PageReader page : info.getPages()) {
addPlaceholderToStack(stack, page.getId(), page.getLabel());
}
MStackElement selectedElement = (MStackElement) modelService.find(info.getActivePageId(), stack);
if (selectedElement != null) {
selectedElement.setToBeRendered(true);
selectedElement.setVisible(true);
}
stack.setSelectedElement(selectedElement);
// restore order of views in the stacks
List<MStackElement> renderedViews = getRenderedViews(stack);
if (renderedViews.size() < 2) {
return;
}
int[] partOrder = info.getPartOrder();
List<MStackElement> stackChildren = stack.getChildren();
// unexpected situation - don't order
if (partOrder == null || partOrder.length != renderedViews.size()) {
return;
}
List<MStackElement> originalOrder = new ArrayList<>(renderedViews);
stackChildren.clear();
for (int element : partOrder) {
stackChildren.add(originalOrder.get(element));
}
originalOrder.removeAll(stackChildren);
stackChildren.addAll(originalOrder);
}
private List<MStackElement> getRenderedViews(MPartStack stack) {
List<MStackElement> renderedViews = new ArrayList<>();
for (MStackElement element : stack.getChildren()) {
if (element.isToBeRendered()) {
renderedViews.add(element);
}
}
return renderedViews;
}
private void populatePartStack(MPartStack stack, DetachedWindowReader info) {
for (PageReader page : info.getPages()) {
addPlaceholderToStack(stack, page.getId(), page.getLabel());
}
stack.setSelectedElement((MStackElement) modelService.find(info.getActivePageId(), stack));
}
private void addPlaceholderToStack(MPartStack stack, String partId, String label) {
if (partId == null || isDefaultFastView(partId)) {
return;
}
MPlaceholder placeholder = createPlaceHolder(partId, label);
if (!isToBeRendered(placeholder)) {
placeholder.setToBeRendered(false);
}
addLayoutTagsToPlaceholder(placeholder, partId);
stack.getChildren().add(placeholder);
viewPlaceholders.put(partId, placeholder);
}
private void addPlaceholderToDefaultFastViewStack(MPartStack stack, String partId, String label) {
MPlaceholder placeholder = createPlaceHolder(partId, label);
if (!isDefaultFastView(placeholder)) {
placeholder.setToBeRendered(false);
}
addLayoutTagsToPlaceholder(placeholder, partId);
stack.getChildren().add(placeholder);
viewPlaceholders.put(partId, placeholder);
}
private void addLayoutTagsToPlaceholder(MPlaceholder placeholder, String partId) {
ViewLayoutReader viewLayout = getViewLayout(partId);
if (viewLayout == null) {
return;
}
List<String> tags = placeholder.getTags();
if (!viewLayout.isCloseable()) {
tags.add(IPresentationEngine.NO_CLOSE);
}
if (viewLayout.isStandalone()) {
tags.add(IPresentationEngine.STANDALONE);
}
}
private boolean isToBeRendered(MPlaceholder placeholder) {
if (renderedViews == null) {
renderedViews = perspReader.getRenderedViewIds();
}
return renderedViews.contains(placeholder.getElementId());
}
private boolean isDefaultFastView(MPlaceholder placeholder) {
if (defaultFastViews == null) {
defaultFastViews = perspReader.getDefaultFastViewBarViewIds();
}
return defaultFastViews.contains(placeholder.getElementId());
}
private boolean isDefaultFastView(String placeholderId) {
if (defaultFastViews == null) {
defaultFastViews = perspReader.getDefaultFastViewBarViewIds();
}
return defaultFastViews.contains(placeholderId);
}
private void addPerspectiveShortcutTags() {
for (String shortcutId : perspReader.getPerspectiveShortcutIds()) {
tags.add(ModeledPageLayout.PERSP_SHORTCUT_TAG + shortcutId);
}
}
private void addActionSetTags() {
for (String actionSetId : perspReader.getActionSetIds()) {
tags.add(ModeledPageLayout.ACTION_SET_TAG + actionSetId);
}
}
private void addShowInTags() {
String origId = null;
if (perspReader.isCustom()) {
origId = perspReader.getBasicPerspectiveId();
} else {
origId = perspReader.getId();
}
ArrayList<String> list = getShowInPartFromRegistry(origId);
if (list != null) {
for (String showIn : list) {
tags.add(ModeledPageLayout.SHOW_IN_PART_TAG + showIn);
}
}
return;
}
public static ArrayList<String> getShowInPartFromRegistry(String targetId) {
ArrayList<String> list = new ArrayList<>();
IExtension[] extensions = getPerspectiveExtensions();
if (extensions != null) {
for (IExtension extension : extensions) {
list.addAll(getExtensionShowInPartFromRegistry(extension, targetId));
}
}
return list;
}
private static IExtension[] getPerspectiveExtensions() {
IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_PERSPECTIVE_EXTENSIONS);
if (point == null) {
return null;
}
IExtension[] extensions = point.getExtensions();
extensions = RegistryReader.orderExtensions(extensions);
return extensions;
}
private static ArrayList<String> getExtensionShowInPartFromRegistry(IExtension extension, String targetId) {
ArrayList<String> list = new ArrayList<>();
IConfigurationElement[] configElements = extension.getConfigurationElements();
for (IConfigurationElement configElement : configElements) {
String type = configElement.getName();
if (type.equals(IWorkbenchRegistryConstants.TAG_PERSPECTIVE_EXTENSION)) {
String id = configElement.getAttribute(IWorkbenchRegistryConstants.ATT_TARGET_ID);
if (targetId.equals(id) || "*".equals(id)) { //$NON-NLS-1$
list.addAll(getConfigElementShowInPartsFromRegistry(configElement));
}
}
}
return list;
}
private static ArrayList<String> getConfigElementShowInPartsFromRegistry(IConfigurationElement configElement) {
ArrayList<String> list = new ArrayList<>();
String tag = IWorkbenchRegistryConstants.TAG_SHOW_IN_PART;
IConfigurationElement[] children = configElement.getChildren();
for (IConfigurationElement child : children) {
String ctype = child.getName();
if (tag.equals(ctype)) {
String tid = child.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
if (tid != null) {
list.add(tid);
}
}
}
return list;
}
private void addNewWizardTags() {
for (String actionId : perspReader.getNewWizardActionIds()) {
tags.add(ModeledPageLayout.NEW_WIZARD_TAG + actionId);
}
}
private void addShowViewTags() {
for (String actionId : perspReader.getShowViewActionIds()) {
tags.add(ModeledPageLayout.SHOW_VIEW_TAG + actionId);
}
}
private void addHiddenItems() {
String comma = ","; //$NON-NLS-1$
StringBuilder persistedValue = new StringBuilder();
for (String elementId : perspReader.getHiddenMenuItemIds()) {
persistedValue.append(ModeledPageLayout.HIDDEN_MENU_PREFIX);
persistedValue.append(elementId).append(comma);
}
for (String elementId : perspReader.getHiddenToolbarItemIds()) {
persistedValue.append(ModeledPageLayout.HIDDEN_TOOLBAR_PREFIX);
persistedValue.append(elementId).append(comma);
}
perspective.getPersistedState().put(ModeledPageLayout.HIDDEN_ITEMS_KEY, persistedValue.toString());
}
private ViewLayoutReader getViewLayout(String viewId) {
if (viewLayouts == null) {
viewLayouts = perspReader.getViewLayouts();
}
return viewLayouts.get(viewId);
}
Collection<MPlaceholder> getPlaceholders() {
return viewPlaceholders.values();
}
MPlaceholder getEditorAreaPlaceholder() {
return editorAreaPlaceholder;
}
MPlaceholder createPlaceHolder(String str, String label) {
MPlaceholder placeholder = null;
placeholder = modelService.createModelElement(MPlaceholder.class);
placeholder.setElementId(str);
if (modelService.getPartDescriptor(str) == null) {
placeholder.getTransientData().put(IWorkbenchConstants.TAG_LABEL, label);
}
return placeholder;
}
}