/******************************************************************************* * Copyright (c) 2010, 2015 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 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 434611, 472654 * Manumitting Technologies Inc - Bug 380609 ******************************************************************************/ package org.eclipse.e4.ui.internal.workbench; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.core.services.log.Logger; import org.eclipse.e4.ui.model.application.MAddon; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.MApplicationElement; import org.eclipse.e4.ui.model.application.commands.MBindingContext; import org.eclipse.e4.ui.model.application.commands.MBindingTable; import org.eclipse.e4.ui.model.application.commands.MCommand; import org.eclipse.e4.ui.model.application.commands.MHandler; import org.eclipse.e4.ui.model.application.commands.MKeyBinding; import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor; import org.eclipse.e4.ui.model.application.ui.MElementContainer; import org.eclipse.e4.ui.model.application.ui.MSnippetContainer; 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.MAdvancedFactory; 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.MPerspectiveStack; import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder; import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory; import org.eclipse.e4.ui.model.application.ui.basic.MPart; 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.MTrimBar; import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow; import org.eclipse.e4.ui.model.application.ui.basic.MWindow; import org.eclipse.e4.ui.model.application.ui.basic.MWindowElement; import org.eclipse.e4.ui.model.application.ui.basic.impl.BasicFactoryImpl; import org.eclipse.e4.ui.model.application.ui.menu.MMenu; import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; import org.eclipse.e4.ui.model.application.ui.menu.MToolControl; import org.eclipse.e4.ui.model.internal.ModelUtils; import org.eclipse.e4.ui.workbench.IPresentationEngine; import org.eclipse.e4.ui.workbench.Selector; 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.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.e4.ui.workbench.modeling.EPlaceholderResolver; import org.eclipse.e4.ui.workbench.modeling.ElementMatcher; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; /** * */ public class ModelServiceImpl implements EModelService { private static String HOSTED_ELEMENT = "HostedElement"; //$NON-NLS-1$ private IEclipseContext appContext; /** Factory which is able to create {@link MApplicationElement}s in a generic way. */ private GenericMApplicationElementFactoryImpl mApplicationElementFactory; // Cleans up after a hosted element is disposed private EventHandler hostedElementHandler = new EventHandler() { @Override public void handleEvent(Event event) { final MUIElement changedElement = (MUIElement) event.getProperty(EventTags.ELEMENT); if (!changedElement.getTags().contains(HOSTED_ELEMENT)) { return; } if (changedElement.getWidget() != null) { return; } EObject eObj = (EObject) changedElement; if (!(eObj.eContainer() instanceof MWindow)) { return; } MWindow hostingWindow = (MWindow) eObj.eContainer(); hostingWindow.getSharedElements().remove(changedElement); changedElement.getTags().remove(HOSTED_ELEMENT); } }; /** * This is a singleton service. One instance is used throughout the running application * * @param appContext * The applicationContext to get the eventBroker from * * @throws NullPointerException * if the given appContext is <code>null</code> */ public ModelServiceImpl(IEclipseContext appContext) { if (appContext == null) { throw new NullPointerException("No application context given!"); //$NON-NLS-1$ } this.appContext = appContext; IEventBroker eventBroker = appContext.get(IEventBroker.class); eventBroker.subscribe(UIEvents.UIElement.TOPIC_WIDGET, hostedElementHandler); mApplicationElementFactory = new GenericMApplicationElementFactoryImpl( appContext.get(IExtensionRegistry.class)); } @Override @SuppressWarnings("unchecked") public final <T extends MApplicationElement> T createModelElement(Class<T> elementType) { if (elementType == null) { throw new NullPointerException("Argument cannot be null."); //$NON-NLS-1$ } T back = (T) mApplicationElementFactory.createEObject(elementType); if (back != null) { return back; } throw new IllegalArgumentException( "Unsupported model object type: " + elementType.getCanonicalName()); //$NON-NLS-1$ } private <T> void findElementsRecursive(MApplicationElement searchRoot, Class<T> clazz, Selector matcher, List<T> elements, int searchFlags) { Assert.isLegal(searchRoot != null); if (searchFlags == 0) { return; } // are *we* a match ? boolean classMatch = clazz == null ? true : clazz.isInstance(searchRoot); if (classMatch && matcher.select(searchRoot)) { if (!elements.contains(searchRoot)) { @SuppressWarnings("unchecked") T element = (T) searchRoot; elements.add(element); } } if (searchRoot instanceof MApplication && (searchFlags == ANYWHERE)) { MApplication app = (MApplication) searchRoot; List<MApplicationElement> children = new ArrayList<>(); if (clazz != null) { if (clazz.equals(MHandler.class)) { children.addAll(app.getHandlers()); } else if (clazz.equals(MCommand.class)) { children.addAll(app.getCommands()); } else if (clazz.equals(MBindingContext.class)) { children.addAll(app.getBindingContexts()); } else if (clazz.equals(MBindingTable.class) || clazz.equals(MKeyBinding.class)) { children.addAll(app.getBindingTables()); } else if (clazz.equals(MAddon.class)) { children.addAll(app.getAddons()); } // } else { only look for these if specifically asked. // children.addAll(app.getHandlers()); // children.addAll(app.getCommands()); // children.addAll(app.getBindingContexts()); // children.addAll(app.getBindingTables()); } for (MApplicationElement child : children) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } if (searchRoot instanceof MBindingContext && (searchFlags == ANYWHERE)) { MBindingContext bindingContext = (MBindingContext) searchRoot; for (MBindingContext child : bindingContext.getChildren()) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } if (searchRoot instanceof MBindingTable) { MBindingTable bindingTable = (MBindingTable) searchRoot; for (MKeyBinding child : bindingTable.getBindings()) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } // Check regular containers if (searchRoot instanceof MElementContainer<?>) { /* * Bug 455281: If given a window with a primary perspective stack, * and we're not told to look outside of the perspectives (i.e., * searchFlags is missing OUTSIDE_PERSPECTIVE), then just search the * primary perspective stack instead. This ignores special areas * like the compat layer's stack holding the Help, CheatSheets, and * Intro. */ MElementContainer<?> searchContainer = (MElementContainer<?>) searchRoot; MPerspectiveStack primaryStack = null; if (searchRoot instanceof MWindow && (searchFlags & OUTSIDE_PERSPECTIVE) == 0 && (primaryStack = getPrimaryPerspectiveStack((MWindow) searchRoot)) != null) { searchContainer = primaryStack; } if (searchContainer instanceof MPerspectiveStack) { if ((searchFlags & IN_ANY_PERSPECTIVE) != 0) { // Search *all* the perspectives MElementContainer<? extends MUIElement> container = searchContainer; List<? extends MUIElement> children = container.getChildren(); for (MUIElement child : children) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } else if ((searchFlags & IN_ACTIVE_PERSPECTIVE) != 0) { // Only search the currently active perspective, if any MPerspective active = ((MPerspectiveStack) searchContainer).getSelectedElement(); if (active != null) { findElementsRecursive(active, clazz, matcher, elements, searchFlags); } } else if ((searchFlags & IN_SHARED_AREA) != 0) { // Only recurse through the shared areas List<MArea> areas = findElements(searchContainer, null, MArea.class, null); for (MArea area : areas) { findElementsRecursive(area, clazz, matcher, elements, searchFlags); } } } else { @SuppressWarnings("unchecked") MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) searchRoot; List<MUIElement> children = container.getChildren(); for (MUIElement child : children) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } } // Search Trim if (searchRoot instanceof MTrimmedWindow && (searchFlags & IN_TRIM) != 0) { MTrimmedWindow tw = (MTrimmedWindow) searchRoot; List<MTrimBar> bars = tw.getTrimBars(); for (MTrimBar bar : bars) { findElementsRecursive(bar, clazz, matcher, elements, searchFlags); } } // Search Detached Windows if (searchRoot instanceof MWindow) { MWindow window = (MWindow) searchRoot; for (MWindow dw : window.getWindows()) { findElementsRecursive(dw, clazz, matcher, elements, searchFlags); } MMenu menu = window.getMainMenu(); if (menu != null && (searchFlags & IN_MAIN_MENU) != 0) { findElementsRecursive(menu, clazz, matcher, elements, searchFlags); } // Check for Handlers if (searchFlags == ANYWHERE && MHandler.class.equals(clazz)) { for (MHandler child : window.getHandlers()) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } } if (searchRoot instanceof MPerspective) { MPerspective persp = (MPerspective) searchRoot; for (MWindow dw : persp.getWindows()) { findElementsRecursive(dw, clazz, matcher, elements, searchFlags); } } // Search shared elements if (searchRoot instanceof MPlaceholder) { MPlaceholder ph = (MPlaceholder) searchRoot; // Don't search in shared areas unless the flag is set if (ph.getRef() != null && (!(ph.getRef() instanceof MArea) || (searchFlags & IN_SHARED_AREA) != 0)) { findElementsRecursive(ph.getRef(), clazz, matcher, elements, searchFlags); } } if (searchRoot instanceof MPart && (searchFlags & IN_PART) != 0) { MPart part = (MPart) searchRoot; for (MMenu menu : part.getMenus()) { findElementsRecursive(menu, clazz, matcher, elements, searchFlags); } MToolBar toolBar = part.getToolbar(); if (toolBar != null) { findElementsRecursive(toolBar, clazz, matcher, elements, searchFlags); } if (MHandler.class.equals(clazz)) { for (MHandler child : part.getHandlers()) { findElementsRecursive(child, clazz, matcher, elements, searchFlags); } } } } /** * If this window has a primary perspective stack, return it. Otherwise * return null. A primary stack is a single MPerspectiveStack found either * as the window's immediate children or the child under a single * MPartSashContainer. * * @param window * the window * @return the stack or {@code null} */ private MPerspectiveStack getPrimaryPerspectiveStack(MWindow window) { List<MWindowElement> winKids = window.getChildren(); if (winKids.isEmpty()) { return null; } // Check if we have a MPerspectiveStack in window's children if (instanceCount(winKids, MPerspectiveStack.class) == 1) { return firstInstance(winKids, MPerspectiveStack.class); } // Traditional shape of IWorkbenchWindow: // MWindow{ MPartSashContainer{ MPerspectiveStack, MPartStack (intro // + cheatsheets + help)}} if (winKids.size() == 1 && winKids.get(0) instanceof MPartSashContainer) { MPartSashContainer topLevelPSC = (MPartSashContainer) winKids.get(0); if (instanceCount(topLevelPSC.getChildren(), MPerspectiveStack.class) == 1) { return firstInstance(topLevelPSC.getChildren(), MPerspectiveStack.class); } } return null; } /** * Return the first element that is an instance of {@code clazz} * * @param elements * @param clazz * @return the first element that is an instanceof {@code clazz} or null */ private <T> T firstInstance(Collection<? super T> elements, Class<T> clazz) { for (Object o : elements) { if (clazz.isInstance(o)) { return clazz.cast(o); } } return null; } /** * Return the number of elements that are an instance of {@code clazz}. * * @param elements * the elements * @param clazz * the class * @return the number of elements that are an instance of {@code clazz} */ private int instanceCount(Collection<?> elements, Class<?> clazz) { int count = 0; for (Object o : elements) { if (clazz.isInstance(o)) { count++; } } return count; } @Override public <T> List<T> findElements(MUIElement searchRoot, String id, Class<T> clazz, List<String> tagsToMatch) { ElementMatcher matcher = new ElementMatcher(id, clazz, tagsToMatch); return findElements(searchRoot, clazz, ANYWHERE, matcher); } @Override public <T> List<T> findElements(MUIElement searchRoot, String id, Class<T> clazz, List<String> tagsToMatch, int searchFlags) { ElementMatcher matcher = new ElementMatcher(id, clazz, tagsToMatch); return findElements(searchRoot, clazz, searchFlags, matcher); } @Override public <T> List<T> findElements(MApplicationElement searchRoot, Class<T> clazz, int searchFlags, Selector matcher) { List<T> elements = new ArrayList<>(); findElementsRecursive(searchRoot, clazz, matcher, elements, searchFlags); return elements; } private <T> List<T> findPerspectiveElements(MUIElement searchRoot, String id, Class<T> clazz, List<String> tagsToMatch) { List<T> elements = new ArrayList<>(); ElementMatcher matcher = new ElementMatcher(id, clazz, tagsToMatch); findElementsRecursive(searchRoot, clazz, matcher, elements, PRESENTATION); return elements; } @Override public MUIElement find(String id, MUIElement searchRoot) { if (id == null || id.length() == 0) { return null; } List<MUIElement> elements = findElements(searchRoot, id, MUIElement.class, null); if (elements.size() > 0) { return elements.get(0); } return null; } @Override public int countRenderableChildren(MUIElement element) { if (!(element instanceof MElementContainer<?>)) { return 0; } @SuppressWarnings("unchecked") MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) element; int count = 0; List<MUIElement> kids = container.getChildren(); for (MUIElement kid : kids) { if (kid.isToBeRendered()) { count++; } } if (element instanceof MPerspective) { MPerspective perspective = (MPerspective) element; for (MWindow window : perspective.getWindows()) { if (window.isToBeRendered()) { count++; } } } return count; } @Override public IEclipseContext getContainingContext(MUIElement element) { return ModelUtils.getContainingContext(element); } @Override public MUIElement cloneElement(MUIElement element, MSnippetContainer snippetContainer) { EObject eObj = (EObject) element; MUIElement clone = (MUIElement) EcoreUtil.copy(eObj); // null out all the references List<MPlaceholder> phList = findElements(clone, null, MPlaceholder.class, null); for (MPlaceholder ph : phList) { // Skip placeholders in the sharedArea int location = getElementLocation(ph); if ((location & IN_SHARED_AREA) != 0) { continue; } ph.setRef(null); } if (snippetContainer != null) { MUIElement snippet = findSnippet(snippetContainer, element.getElementId()); if (snippet != null) { snippetContainer.getSnippets().remove(snippet); } snippetContainer.getSnippets().add(clone); } // Cache the original element in the clone's transientData clone.getTransientData().put(CLONED_FROM_KEY, element); return clone; } @Override public MUIElement cloneSnippet(MSnippetContainer snippetContainer, String snippetId, MWindow refWin) { if (snippetContainer == null || snippetId == null || snippetId.length() == 0) { return null; } MApplicationElement elementToClone = null; for (MApplicationElement snippet : snippetContainer.getSnippets()) { if (snippetId.equals(snippet.getElementId())) { elementToClone = snippet; break; } } if (elementToClone == null) { return null; } EObject eObj = (EObject) elementToClone; MUIElement element = (MUIElement) EcoreUtil.copy(eObj); MUIElement appElement = refWin == null ? null : refWin.getParent(); if (appElement instanceof MApplication) { getNullRefPlaceHolders(element, refWin, true); } return element; } private List<MPlaceholder> getNullRefPlaceHolders(MUIElement element, MWindow refWin, boolean resolveAlways) { // use appContext as MApplication.getContext() is null during the processing of // the model processor classes EPlaceholderResolver resolver = appContext.get(EPlaceholderResolver.class); // Re-resolve any placeholder references List<MPlaceholder> phList = findElements(element, null, MPlaceholder.class, null); List<MPlaceholder> nullRefList = new ArrayList<>(); for (MPlaceholder ph : phList) { if (resolveAlways) { resolver.resolvePlaceholderRef(ph, refWin); } else if ((!resolveAlways) && (ph.getRef() == null)) { resolver.resolvePlaceholderRef(ph, refWin); MUIElement partElement = ph.getRef(); if (partElement instanceof MPart) { MPart part = (MPart) partElement; if (part.getIconURI() == null) { MPartDescriptor desc = getPartDescriptor(part.getElementId()); if (desc != null) { part.setIconURI(desc.getIconURI()); } } } } if (ph.getRef() == null) { nullRefList.add(ph); } } return nullRefList; } /** * @param element * @param refWin * @return list of null referencing place holders */ public List<MPlaceholder> getNullRefPlaceHolders(MUIElement element, MWindow refWin) { return getNullRefPlaceHolders(element, refWin, false); } @Override public MUIElement findSnippet(MSnippetContainer snippetContainer, String id) { if (snippetContainer == null || id == null || id.length() == 0) { return null; } List<MUIElement> snippets = snippetContainer.getSnippets(); for (MUIElement snippet : snippets) { if (id.equals(snippet.getElementId())) { return snippet; } } return null; } @Override public void bringToTop(MUIElement element) { if (element instanceof MApplication) { return; } MWindow window = getTopLevelWindowFor(element); if (window == element) { if (!element.isToBeRendered()) { element.setToBeRendered(true); } window.getParent().setSelectedElement(window); } else { showElementInWindow(window, element); } UIEvents.publishEvent(UIEvents.UILifeCycle.BRINGTOTOP, element); } private void showElementInWindow(MWindow window, MUIElement element) { MUIElement parent = element.getParent(); if (parent == null) { MPlaceholder ph = findPlaceholderFor(window, element); if (ph != null) { element = ph; parent = element.getParent(); } } if (parent == null && element instanceof MWindow) { // no parent but is a window, could be a detached window then parent = (MUIElement) ((EObject) element).eContainer(); if (parent != null) { // Force the element to be rendered if (!element.isToBeRendered()) { element.setToBeRendered(true); } if (window != parent) { showElementInWindow(window, parent); } } } else if (parent != null) { // Force the element to be rendered if (!element.isToBeRendered()) { element.setToBeRendered(true); } @SuppressWarnings("unchecked") MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) parent; container.setSelectedElement(element); if (window != parent) { showElementInWindow(window, parent); } } } @Override public MPlaceholder findPlaceholderFor(MWindow window, MUIElement element) { List<MPlaceholder> phList = findPerspectiveElements(window, null, MPlaceholder.class, null); List<MPlaceholder> elementRefs = new ArrayList<>(); for (MPlaceholder ph : phList) { if (ph.getRef() == element) { elementRefs.add(ph); } } if (elementRefs.size() == 0) { return null; } if (elementRefs.size() == 1) { return elementRefs.get(0); } // If there is more than one placeholder then return the one in the shared area for (MPlaceholder refPh : elementRefs) { int loc = getElementLocation(refPh); if ((loc & IN_SHARED_AREA) != 0) { return refPh; } } // Just return the first one return elementRefs.get(0); } @Override public <T extends MUIElement> void move(T element, MElementContainer<? super T> newParent) { move(element, newParent, -1, false); } @Override public <T extends MUIElement> void move(T element, MElementContainer<? super T> newParent, boolean leavePlaceholder) { move(element, newParent, -1, leavePlaceholder); } @Override public <T extends MUIElement> void move(T element, MElementContainer<? super T> newParent, int index) { move(element, newParent, index, false); } @Override public <T extends MUIElement> void move(T element, MElementContainer<? super T> newParent, int index, boolean leavePlaceholder) { // Cache where we were MElementContainer<MUIElement> curParent = element.getParent(); int curIndex = curParent.getChildren().indexOf(element); // Move the model element if (index == -1) { newParent.getChildren().add(element); } else { newParent.getChildren().add(index, element); } if (leavePlaceholder) { MPlaceholder ph = MAdvancedFactory.INSTANCE.createPlaceholder(); ph.setRef(element); curParent.getChildren().add(curIndex, ph); } } private void combine(MPartSashContainerElement toInsert, MPartSashContainerElement relTo, MPartSashContainer newSash, boolean newFirst, float ratio) { MElementContainer<MUIElement> curParent = relTo.getParent(); if (curParent == null) { // if relTo is a shared element, use its current placeholder MWindow win = getTopLevelWindowFor(relTo); relTo = findPlaceholderFor(win, relTo); curParent = relTo.getParent(); } Assert.isLegal(relTo != null && curParent != null); int index = curParent.getChildren().indexOf(relTo); curParent.getChildren().remove(relTo); if (newFirst) { newSash.getChildren().add(toInsert); newSash.getChildren().add(relTo); } else { newSash.getChildren().add(relTo); newSash.getChildren().add(toInsert); } // Set up the container data before adding the new sash to the model // To raise the granularity assume 100% == 10,000 int adjustedPct = (int) (ratio * 10000); toInsert.setContainerData(Integer.toString(adjustedPct)); relTo.setContainerData(Integer.toString(10000 - adjustedPct)); // add the new sash at the same location curParent.getChildren().add(index, newSash); } @Override public void insert(MPartSashContainerElement toInsert, MPartSashContainerElement relTo, int where, float ratio) { assert (toInsert != null && relTo != null); if (ratio >= 1) { warn("EModelService#insert() expects the ratio to be between (0,100)"); //$NON-NLS-1$ ratio = ratio / 100; // reduce it } assert(ratio > 0 && ratio < 1); // determine insertion order boolean insertBefore = where == ABOVE || where == LEFT_OF; boolean horizontal = where == LEFT_OF || where == RIGHT_OF; MPartSashContainer newSash = BasicFactoryImpl.eINSTANCE.createPartSashContainer(); newSash.setHorizontal(horizontal); // Maintain the existing weight in the new sash newSash.setContainerData(relTo.getContainerData()); combine(toInsert, relTo, newSash, insertBefore, ratio); } @Override public void detach(MPartSashContainerElement element, int x, int y, int width, int height) { // If we're showing through a placehoilder then detach it... if (element.getCurSharedRef() != null) { element = element.getCurSharedRef(); } // Determine the correct parent for the new window MWindow window = getTopLevelWindowFor(element); MPerspective thePersp = getPerspectiveFor(element); MTrimmedWindow newWindow = MBasicFactory.INSTANCE.createTrimmedWindow(); newWindow.setX(x); newWindow.setY(y); newWindow.setWidth(width); newWindow.setHeight(height); element.getParent().getChildren().remove(element); MWindowElement uiRoot = wrapElementForWindow(element); newWindow.getChildren().add(uiRoot); if (thePersp != null) { thePersp.getWindows().add(newWindow); } else if (window != null) { window.getWindows().add(newWindow); } } /** * Wraps an element in a PartStack if it's a MPart or an MPlaceholder that references an MPart * * @param element * The element to be wrapped * @return The wrapper for the given element */ private MWindowElement wrapElementForWindow(MPartSashContainerElement element) { if (element instanceof MPlaceholder) { MUIElement ref = ((MPlaceholder) element).getRef(); if (ref instanceof MPart) { MPartStack newPS = MBasicFactory.INSTANCE.createPartStack(); newPS.getChildren().add((MPlaceholder) element); return newPS; } } else if (element instanceof MPart) { MPartStack newPS = MBasicFactory.INSTANCE.createPartStack(); newPS.getChildren().add((MPart) element); return newPS; } else if (element instanceof MWindowElement) { return (MWindowElement) element; } return null; } @Override public MTrimBar getTrim(MTrimmedWindow window, SideValue sv) { List<MTrimBar> bars = window.getTrimBars(); for (MTrimBar bar : bars) { if (bar.getSide() == sv) { return bar; } } // Didn't find a trim bar for the side, make one MTrimBar newBar = BasicFactoryImpl.eINSTANCE.createTrimBar(); // Assign default ids to the trim bars to match legacy eclipse if (sv == SideValue.TOP) { newBar.setElementId("org.eclipse.ui.main.menu"); //$NON-NLS-1$ } else if (sv == SideValue.BOTTOM) { newBar.setElementId("org.eclipse.ui.trim.status"); //$NON-NLS-1$ } else if (sv == SideValue.LEFT) { newBar.setElementId("org.eclipse.ui.trim.vertical1"); //$NON-NLS-1$ } else if (sv == SideValue.RIGHT) { newBar.setElementId("org.eclipse.ui.trim.vertical2"); //$NON-NLS-1$ } newBar.setSide(sv); window.getTrimBars().add(newBar); return newBar; } @Override public MWindow getTopLevelWindowFor(MUIElement element) { EObject eObj = (EObject) element; while (eObj != null && !(eObj.eContainer() instanceof MApplication)) { eObj = eObj.eContainer(); } if (eObj instanceof MWindow) { return (MWindow) eObj; } return null; // Ooops! } @Override public MPerspective getPerspectiveFor(MUIElement element) { while (true) { // if we have a placeholder, reassign ourselves MPlaceholder placeholder = element.getCurSharedRef(); if (placeholder != null) { element = placeholder; } EObject container = ((EObject) element).eContainer(); if (container == null || container instanceof MApplication) { // climbed to the top and found nothing, return null return null; } else if (container instanceof MPerspectiveStack) { // parent is a perspective stack, we ourselves should be a perspective return (MPerspective) element; } // climb up element = (MUIElement) container; } } @Override public void resetPerspectiveModel(MPerspective persp, MWindow window) { resetPerspectiveModel(persp, window, true); } private void resetPerspectiveModel(MPerspective persp, MWindow window, boolean removeSharedPlaceholders) { if (persp == null) { return; } if (removeSharedPlaceholders) { // Remove any views (Placeholders) from the shared area EPartService ps = window.getContext().get(EPartService.class); List<MArea> areas = findElements(window, null, MArea.class, null); if (areas.size() == 1) { MArea area = areas.get(0); // Strip out the placeholders in visible stacks List<MPlaceholder> phList = findElements(area, null, MPlaceholder.class, null); for (MPlaceholder ph : phList) { ps.hidePart((MPart) ph.getRef()); ph.getParent().getChildren().remove(ph); } // Prevent shared stacks ids from clashing with the ones in the perspective List<MPartStack> stacks = findElements(area, null, MPartStack.class, null); for (MPartStack stack : stacks) { String generatedId = "PartStack@" + Integer.toHexString(stack.hashCode()); //$NON-NLS-1$ stack.setElementId(generatedId); } // Also remove any min/max tags on the area (or its placeholder) MUIElement areaPresentation = area; if (area.getCurSharedRef() != null) { areaPresentation = area.getCurSharedRef(); } areaPresentation.getTags().remove(IPresentationEngine.MAXIMIZED); areaPresentation.getTags().remove(IPresentationEngine.MINIMIZED); areaPresentation.getTags().remove(IPresentationEngine.MINIMIZED_BY_ZOOM); } } // Remove any minimized stacks for this perspective List<MTrimBar> bars = findElements(window, null, MTrimBar.class, null); List<MToolControl> toRemove = new ArrayList<>(); for (MTrimBar bar : bars) { for (MUIElement barKid : bar.getChildren()) { if (!(barKid instanceof MToolControl)) { continue; } String id = barKid.getElementId(); if (id != null && id.contains(persp.getElementId())) { toRemove.add((MToolControl) barKid); } } } for (MToolControl toolControl : toRemove) { // Close any open fast view toolControl.setToBeRendered(false); toolControl.getParent().getChildren().remove(toolControl); } } @Override public void removePerspectiveModel(MPerspective persp, MWindow window) { // pick a new perspective to become active (if any) MUIElement psElement = persp.getParent(); MPerspectiveStack ps = (MPerspectiveStack) psElement; boolean foundNewSelection = false; if (ps.getSelectedElement() == persp) { for (MPerspective p : ps.getChildren()) { if (p != persp && p.isToBeRendered()) { ps.setSelectedElement(p); foundNewSelection = true; break; } } if (!foundNewSelection) { ps.setSelectedElement(null); } } // Remove transient elements (minimized stacks, detached windows) resetPerspectiveModel(persp, window, false); // unrender the perspective and remove it persp.setToBeRendered(false); ps.getChildren().remove(persp); } @Override public MPerspective getActivePerspective(MWindow window) { List<MPerspectiveStack> pStacks = findElements(window, null, MPerspectiveStack.class, null); if (pStacks.size() == 1) { MPerspective perspective = pStacks.get(0).getSelectedElement(); return perspective; } return null; } @Override public int toBeRenderedCount(MElementContainer<?> container) { int count = 0; for (MUIElement child : container.getChildren()) { if (child.isToBeRendered()) { count++; } } return count; } @Override public MUIElement getContainer(MUIElement element) { if (element == null) { return null; } return (MUIElement) ((EObject) element).eContainer(); } @Override public int getElementLocation(MUIElement element) { if (element == null) { return NOT_IN_UI; } // If the element is shared then use its current placeholder if (element.getCurSharedRef() != null) { element = element.getCurSharedRef(); } int location = NOT_IN_UI; MUIElement curElement = element; while (curElement != null) { Object container = ((EObject) curElement).eContainer(); if (!(container instanceof MUIElement)) return NOT_IN_UI; if (container instanceof MApplication) { if (location != NOT_IN_UI) return location; return OUTSIDE_PERSPECTIVE; } else if (container instanceof MPerspective) { MPerspective perspective = (MPerspective) container; MUIElement perspParent = perspective.getParent(); if (perspParent == null) { location = NOT_IN_UI; } else if (perspective.getParent().getSelectedElement() == perspective) { location |= IN_ACTIVE_PERSPECTIVE; } else { location |= IN_ANY_PERSPECTIVE; } } else if (container instanceof MTrimBar) { location = IN_TRIM; } else if (container instanceof MArea) { location = IN_SHARED_AREA; } curElement = (MUIElement) container; } return NOT_IN_UI; } @Override public MPartDescriptor getPartDescriptor(String id) { MApplication application = appContext.get(MApplication.class); // If the id contains a ':' use the part before it as the descriptor id int colonIndex = id == null ? -1 : id.indexOf(':'); String descId = colonIndex == -1 ? id : id.substring(0, colonIndex); for (MPartDescriptor descriptor : application.getDescriptors()) { if (descriptor.getElementId().equals(descId)) { return descriptor; } } return null; } @Override public MPart createPart(MPartDescriptor descriptor) { if (descriptor == null) { return null; } MPart part = createModelElement(MPart.class); part.setElementId(descriptor.getElementId()); part.getMenus().addAll(EcoreUtil.copyAll(descriptor.getMenus())); if (descriptor.getToolbar() != null) { part.setToolbar((MToolBar) EcoreUtil.copy((EObject) descriptor.getToolbar())); } part.setContributorURI(descriptor.getContributorURI()); part.setCloseable(descriptor.isCloseable()); part.setContributionURI(descriptor.getContributionURI()); part.setLabel(descriptor.getLabel()); part.setIconURI(descriptor.getIconURI()); part.setTooltip(descriptor.getTooltip()); part.getHandlers().addAll(EcoreUtil.copyAll(descriptor.getHandlers())); part.getTags().addAll(descriptor.getTags()); part.getVariables().addAll(descriptor.getVariables()); part.getProperties().putAll(descriptor.getProperties()); part.getPersistedState().putAll(descriptor.getPersistedState()); part.getBindingContexts().addAll(descriptor.getBindingContexts()); if (descriptor.getTrimBars() != null) { part.getTrimBars().addAll(EcoreUtil.copyAll(descriptor.getTrimBars())); } return part; } @Override public void hideLocalPlaceholders(MWindow window, MPerspective perspective) { List<MPlaceholder> globals = findElements(window, null, MPlaceholder.class, null, OUTSIDE_PERSPECTIVE | IN_SHARED_AREA); // Iterate across the perspective(s) removing any 'local' placeholders List<MPerspective> persps = new ArrayList<>(); if (perspective != null) { persps.add(perspective); } else { persps = findElements(window, null, MPerspective.class, null); } for (MPerspective persp : persps) { List<MPlaceholder> locals = findElements(persp, null, MPlaceholder.class, null, IN_ANY_PERSPECTIVE); for (MPlaceholder local : locals) { for (MPlaceholder global : globals) { if (global.getRef() == local.getRef()) { local.setToBeRendered(false); MElementContainer<MUIElement> localParent = local.getParent(); setStackVisibility(localParent); } } } } } /** * @param parent */ private void setStackVisibility(MElementContainer<MUIElement> parent) { for (MUIElement child : parent.getChildren()) { if (child.isToBeRendered() && child.isVisible()) { parent.setToBeRendered(true); return; } } parent.setToBeRendered(false); // continue modifying the visibility as the parent's parent may also // need to be hidden from the user setStackVisibility(parent.getParent()); } @Override public boolean isLastEditorStack(MUIElement stack) { if (!(stack instanceof MPartStack)) { return false; } // is it in the shared area? MUIElement parent = stack.getParent(); while (parent != null && !(parent instanceof MArea)) { parent = parent.getParent(); } if (parent == null) { return false; } // OK, it's in the area, is it the last TBR one ? MArea area = (MArea) parent; List<MPartStack> stacks = findElements(area, null, MPartStack.class, null); int count = 0; for (MPartStack aStack : stacks) { if (aStack.isToBeRendered()) { count++; } } return count < 2 && stack.isToBeRendered(); } @Override public void hostElement(MUIElement element, MWindow hostWindow, Object uiContainer, IEclipseContext hostContext) { // This is subtle; unless the element is hooked into the model it won't fire events hostWindow.getSharedElements().add(element); element.getTags().add(HOSTED_ELEMENT); IPresentationEngine renderer = hostWindow.getContext().get(IPresentationEngine.class); renderer.createGui(element, uiContainer, hostContext); } @Override public boolean isHostedElement(MUIElement element, MWindow hostWindow) { MUIElement curElement = element; while (curElement != null && !curElement.getTags().contains(HOSTED_ELEMENT)) { if (curElement.getCurSharedRef() != null) { curElement = curElement.getCurSharedRef(); } else { curElement = curElement.getParent(); } } if (curElement == null) { return false; } return hostWindow.getSharedElements().contains(curElement); } private void warn(String message) { Logger logger = appContext.get(Logger.class); if (logger != null) { logger.warn(message); } } }