| /******************************************************************************* |
| * Copyright (c) 2009, 2013 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.e4.ui.internal.workbench; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import javax.annotation.PostConstruct; |
| import javax.annotation.PreDestroy; |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.e4.core.contexts.ContextInjectionFactory; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.di.InjectionException; |
| import org.eclipse.e4.core.di.annotations.Optional; |
| import org.eclipse.e4.core.services.events.IEventBroker; |
| import org.eclipse.e4.core.services.log.Logger; |
| import org.eclipse.e4.ui.di.Persist; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor; |
| import org.eclipse.e4.ui.model.application.ui.MContext; |
| import org.eclipse.e4.ui.model.application.ui.MElementContainer; |
| import org.eclipse.e4.ui.model.application.ui.MGenericStack; |
| 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.advanced.impl.AdvancedFactoryImpl; |
| import org.eclipse.e4.ui.model.application.ui.basic.MInputPart; |
| 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.MWindow; |
| import org.eclipse.e4.ui.model.application.ui.basic.impl.BasicFactoryImpl; |
| import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; |
| import org.eclipse.e4.ui.services.EContextService; |
| import org.eclipse.e4.ui.services.IServiceConstants; |
| import org.eclipse.e4.ui.workbench.IPresentationEngine; |
| import org.eclipse.e4.ui.workbench.UIEvents; |
| import org.eclipse.e4.ui.workbench.modeling.EModelService; |
| import org.eclipse.e4.ui.workbench.modeling.EPartService; |
| import org.eclipse.e4.ui.workbench.modeling.IPartListener; |
| import org.eclipse.e4.ui.workbench.modeling.ISaveHandler; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventHandler; |
| |
| public class PartServiceImpl implements EPartService { |
| |
| private EventHandler selectedHandler = new EventHandler() { |
| public void handleEvent(Event event) { |
| // no need to do anything if we have no listeners |
| if (!listeners.isEmpty()) { |
| Object oldSelected = event.getProperty(UIEvents.EventTags.OLD_VALUE); |
| if (oldSelected instanceof MPlaceholder) { |
| oldSelected = ((MPlaceholder) oldSelected).getRef(); |
| } |
| Object selected = event.getProperty(UIEvents.EventTags.NEW_VALUE); |
| if (selected instanceof MPlaceholder) { |
| selected = ((MPlaceholder) selected).getRef(); |
| } |
| |
| MPart oldSelectedPart = oldSelected instanceof MPart ? (MPart) oldSelected : null; |
| MPart selectedPart = selected instanceof MPart ? (MPart) selected : null; |
| |
| if (oldSelectedPart != null && getParts().contains(selectedPart)) { |
| firePartHidden(oldSelectedPart); |
| } |
| |
| if (selectedPart != null && selectedPart.isToBeRendered() |
| && getParts().contains(selectedPart)) { |
| MPlaceholder placeholder = selectedPart.getCurSharedRef(); |
| // ask the renderer to create this part |
| if (placeholder == null) { |
| if (selectedPart.getParent().getRenderer() != null) { |
| engine.createGui(selectedPart); |
| firePartVisible(selectedPart); |
| firePartBroughtToTop(selectedPart); |
| } |
| } else if (placeholder.getParent().getRenderer() != null) { |
| engine.createGui(placeholder); |
| firePartVisible(selectedPart); |
| firePartBroughtToTop(selectedPart); |
| } |
| } |
| } |
| } |
| }; |
| |
| private MApplication application; |
| |
| /** |
| * Might be null if this part service is created for the application |
| */ |
| private MWindow workbenchWindow; |
| |
| @Inject |
| private IPresentationEngine engine; |
| |
| @Inject |
| private EModelService modelService; |
| |
| @Inject |
| private Logger logger; |
| |
| @Inject |
| @Optional |
| private ISaveHandler saveHandler; |
| |
| @Inject |
| private IEventBroker eventBroker; |
| |
| // @Optional as the context service may not have been installed |
| @Inject |
| @Optional |
| private EContextService contextService; |
| |
| private PartActivationHistory partActivationHistory; |
| |
| private MPart activePart; |
| |
| private ListenerList listeners = new ListenerList(); |
| |
| private boolean constructed = false; |
| |
| @Inject |
| public PartServiceImpl(MApplication application, @Optional MWindow window) { |
| // no need to track changes: |
| this.application = application; |
| workbenchWindow = window; |
| } |
| |
| private void log(String unidentifiedMessage, String identifiedMessage, String id, Exception e) { |
| if (id == null || id.length() == 0) { |
| logger.error(e, unidentifiedMessage); |
| } else { |
| logger.error(e, NLS.bind(identifiedMessage, id)); |
| } |
| } |
| |
| @Inject |
| void setPart(@Optional @Named(IServiceConstants.ACTIVE_PART) MPart p) { |
| if (activePart != p) { |
| activate(p, true, true); |
| } |
| } |
| |
| @PostConstruct |
| void postConstruct() { |
| eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT, selectedHandler); |
| constructed = true; |
| partActivationHistory = new PartActivationHistory(this, modelService); |
| if (activePart != null) { |
| partActivationHistory.prepend(activePart); |
| } |
| } |
| |
| @PreDestroy |
| void preDestroy() { |
| constructed = false; |
| eventBroker.unsubscribe(selectedHandler); |
| partActivationHistory.clear(); |
| } |
| |
| private void firePartActivated(final MPart part) { |
| for (final Object listener : listeners.getListeners()) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| ((IPartListener) listener).partActivated(part); |
| } |
| |
| public void handleException(Throwable throwable) { |
| logger.error(throwable, "An exception occurred while notifying part listeners"); //$NON-NLS-1$ |
| } |
| }); |
| } |
| } |
| |
| private void firePartDeactivated(final MPart part) { |
| for (final Object listener : listeners.getListeners()) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| ((IPartListener) listener).partDeactivated(part); |
| } |
| |
| public void handleException(Throwable throwable) { |
| logger.error(throwable, "An exception occurred while notifying part listeners"); //$NON-NLS-1$ |
| } |
| }); |
| } |
| } |
| |
| private void firePartHidden(final MPart part) { |
| for (final Object listener : listeners.getListeners()) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| ((IPartListener) listener).partHidden(part); |
| } |
| |
| public void handleException(Throwable throwable) { |
| logger.error(throwable, "An exception occurred while notifying part listeners"); //$NON-NLS-1$ |
| } |
| }); |
| } |
| } |
| |
| private void firePartVisible(final MPart part) { |
| for (final Object listener : listeners.getListeners()) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| ((IPartListener) listener).partVisible(part); |
| } |
| |
| public void handleException(Throwable throwable) { |
| logger.error(throwable, "An exception occurred while notifying part listeners"); //$NON-NLS-1$ |
| } |
| }); |
| } |
| } |
| |
| private void firePartBroughtToTop(final MPart part) { |
| for (final Object listener : listeners.getListeners()) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| ((IPartListener) listener).partBroughtToTop(part); |
| } |
| |
| public void handleException(Throwable throwable) { |
| logger.error(throwable, "An exception occurred while notifying part listeners"); //$NON-NLS-1$ |
| } |
| }); |
| } |
| } |
| |
| public void addPartListener(IPartListener listener) { |
| listeners.add(listener); |
| } |
| |
| public void removePartListener(IPartListener listener) { |
| listeners.remove(listener); |
| } |
| |
| private MWindow getWindow() { |
| if (workbenchWindow != null) |
| return workbenchWindow; |
| if (application.getSelectedElement() != null) |
| return application.getSelectedElement(); |
| List<MWindow> windows = application.getChildren(); |
| if (windows.size() != 0) |
| return windows.get(0); |
| return null; |
| } |
| |
| private MContext getParentWithContext(MUIElement part) { |
| MElementContainer<MUIElement> parent = part.getParent(); |
| MUIElement intermediate = parent; |
| if (intermediate == null) { |
| intermediate = part; |
| } else { |
| while (parent != null) { |
| if (parent instanceof MContext) { |
| if (((MContext) parent).getContext() != null) |
| return (MContext) parent; |
| } |
| intermediate = parent; |
| parent = parent.getParent(); |
| } |
| } |
| |
| MPlaceholder placeholder = modelService.findPlaceholderFor(getWindow(), intermediate); |
| parent = placeholder.getParent(); |
| while (parent != null) { |
| if (parent instanceof MContext) { |
| if (((MContext) parent).getContext() != null) |
| return (MContext) parent; |
| } |
| parent = parent.getParent(); |
| } |
| return null; |
| } |
| |
| public void bringToTop(MPart part) { |
| if (isInContainer(part)) { |
| MUIElement currentElement = part; |
| MElementContainer<MUIElement> parent = part.getParent(); |
| if (parent == null) { |
| currentElement = modelService.findPlaceholderFor(getWindow(), part); |
| parent = currentElement.getParent(); |
| } |
| |
| // If the part is in the same stack as the currently active part then activate it |
| // instead |
| MElementContainer<MUIElement> activeParent = activePart != null ? activePart |
| .getParent() : null; |
| if (activePart != null && activeParent == null) { |
| MPlaceholder activePH = modelService.findPlaceholderFor(getWindow(), activePart); |
| if (activePH != null) { |
| activeParent = activePH.getParent(); |
| } |
| } |
| if (parent == activeParent && part != activePart) { |
| activate(part); |
| return; |
| } |
| |
| MUIElement oldSelectedElement = parent.getSelectedElement(); |
| |
| delegateBringToTop(part); |
| |
| // check to make sure that the currently selected element is actually valid |
| if (oldSelectedElement != currentElement |
| && parent.getChildren().contains(oldSelectedElement) |
| && parent instanceof MGenericStack<?>) { |
| if (oldSelectedElement instanceof MPlaceholder) { |
| oldSelectedElement = ((MPlaceholder) oldSelectedElement).getRef(); |
| } |
| internalFixContext(part, oldSelectedElement); |
| } |
| } |
| } |
| |
| private IEclipseContext getSubContext(MUIElement element) { |
| if (element instanceof MContext) { |
| return ((MContext) element).getContext(); |
| } else if (element instanceof MElementContainer<?>) { |
| Object selectedElement = ((MElementContainer<?>) element).getSelectedElement(); |
| if (selectedElement instanceof MContext) { |
| return ((MContext) selectedElement).getContext(); |
| } else if (selectedElement instanceof MElementContainer<?>) { |
| return getSubContext((MUIElement) selectedElement); |
| } |
| } |
| return null; |
| } |
| |
| private void internalFixContext(MPart part, MUIElement oldSelectedElement) { |
| if (oldSelectedElement == null) { |
| return; |
| } |
| |
| MContext parentPart = getParentWithContext(oldSelectedElement); |
| if (parentPart == null) { |
| // technically this shouldn't happen as there should be an MWindow somewhere |
| return; |
| } |
| IEclipseContext parentContext = parentPart.getContext(); |
| IEclipseContext oldContext = getSubContext(oldSelectedElement); |
| Object child = parentContext.getActiveChild(); |
| if (child == null || oldContext == null || child == oldContext) { |
| if (part == null) { |
| // TBD this should not be necessary; deactivation is missing somewhere |
| IEclipseContext currentActive = parentContext.getActiveChild(); |
| if (currentActive != null) |
| currentActive.deactivate(); |
| } else |
| part.getContext().activate(); |
| } |
| } |
| |
| public MPart findPart(String id) { |
| List<MPart> parts = getParts(MPart.class, id); |
| return parts.size() > 0 ? parts.get(0) : null; |
| } |
| |
| private <T> List<T> getParts(Class<T> cls, String id) { |
| return modelService.findElements(workbenchWindow, id, cls, null, |
| EModelService.OUTSIDE_PERSPECTIVE | EModelService.IN_ACTIVE_PERSPECTIVE |
| | EModelService.IN_SHARED_AREA); |
| } |
| |
| public Collection<MPart> getParts() { |
| return getParts(MPart.class, null); |
| } |
| |
| public boolean isPartVisible(MPart part) { |
| if (isInContainer(part)) { |
| MUIElement element = part; |
| MElementContainer<?> parent = part.getParent(); |
| if (parent == null) { |
| // might be a shared part |
| element = part.getCurSharedRef(); |
| if (element == null) { |
| return false; |
| } |
| |
| parent = element.getParent(); |
| if (parent == null) { |
| return false; |
| } |
| } |
| |
| if (parent instanceof MPartStack) { |
| return parent.getSelectedElement() == element; |
| } |
| |
| return element.isVisible(); |
| } |
| return false; |
| } |
| |
| private boolean isInContainer(MUIElement element) { |
| if (modelService.isHostedElement(element, getWindow())) |
| return true; |
| List<MUIElement> allPerspectiveElements = modelService.findElements(workbenchWindow, null, |
| MUIElement.class, null, EModelService.PRESENTATION); |
| return allPerspectiveElements.contains(element); |
| } |
| |
| boolean isInContainer(MElementContainer<?> container, MUIElement element) { |
| for (Object object : container.getChildren()) { |
| if (object == element) { |
| return true; |
| } else if (object instanceof MElementContainer<?>) { |
| if (isInContainer((MElementContainer<?>) object, element)) { |
| return true; |
| } |
| } else if (object instanceof MPlaceholder) { |
| MUIElement ref = ((MPlaceholder) object).getRef(); |
| if (ref == element) { |
| return true; |
| } else if (ref instanceof MElementContainer<?>) { |
| if (isInContainer((MElementContainer<?>) ref, element)) { |
| return true; |
| } |
| } |
| } else if (object instanceof MPerspective) { |
| MPerspective persp = (MPerspective) object; |
| for (MWindow dw : persp.getWindows()) { |
| if (isInContainer(dw, element)) |
| return true; |
| } |
| } else if (object instanceof MWindow) { |
| MWindow win = (MWindow) object; |
| for (MWindow dw : win.getWindows()) { |
| if (isInContainer(dw, element)) |
| return true; |
| } |
| } |
| } |
| |
| if (container instanceof MWindow) { |
| MWindow win = (MWindow) container; |
| for (MWindow dw : win.getWindows()) { |
| if (isInContainer(dw, element)) |
| return true; |
| } |
| } |
| |
| if (container instanceof MPerspective) { |
| MPerspective persp = (MPerspective) container; |
| for (MWindow dw : persp.getWindows()) { |
| if (isInContainer(dw, element)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| MPlaceholder getLocalPlaceholder(MUIElement part) { |
| return modelService.findPlaceholderFor(getWindow(), part); |
| } |
| |
| public void switchPerspective(MPerspective perspective) { |
| Assert.isNotNull(perspective); |
| MWindow window = getWindow(); |
| if (window != null && isInContainer(window, perspective)) { |
| perspective.getParent().setSelectedElement(perspective); |
| List<MPart> newPerspectiveParts = modelService.findElements(perspective, null, |
| MPart.class, null); |
| // if possible, keep the same active part across perspective switches |
| if (newPerspectiveParts.contains(activePart) |
| && partActivationHistory.isValid(perspective, activePart)) { |
| MPart target = activePart; |
| IEclipseContext activeChild = activePart.getContext().getParent().getActiveChild(); |
| if (activeChild != null) { |
| activeChild.deactivate(); |
| } |
| perspective.getContext().activate(); |
| modelService.bringToTop(target); |
| activate(target, true, false); |
| return; |
| } |
| |
| MPart newActivePart = perspective.getContext().getActiveLeaf().get(MPart.class); |
| if (newActivePart == null) { |
| // whatever part was previously active can no longer be found, find another one |
| MPart candidate = partActivationHistory.getActivationCandidate(perspective); |
| if (candidate != null) { |
| modelService.bringToTop(candidate); |
| activate(candidate, true, false); |
| return; |
| } |
| } |
| |
| // there seems to be no parts in this perspective, just activate it as is then |
| if (newActivePart == null) { |
| modelService.bringToTop(perspective); |
| perspective.getContext().activate(); |
| } else { |
| activate(newActivePart, true, false); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.e4.ui.workbench.modeling.EPartService#activate(org.eclipse.e4.ui.model.application |
| * .MPart) |
| */ |
| public void activate(MPart part) { |
| activate(part, true); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.e4.ui.workbench.modeling.EPartService#activate(org.eclipse.e4.ui.model.application |
| * .MPart,boolean) |
| */ |
| public void activate(MPart part, boolean requiresFocus) { |
| activate(part, requiresFocus, true); |
| } |
| |
| private void activate(MPart part, boolean requiresFocus, boolean activateBranch) { |
| if (part == null) { |
| if (constructed && activePart != null) { |
| firePartDeactivated(activePart); |
| } |
| activePart = part; |
| return; |
| } |
| |
| // only activate parts that is under our control |
| if (!isInContainer(part)) { |
| return; |
| } |
| |
| MWindow window = getWindow(); |
| IEclipseContext windowContext = window.getContext(); |
| // check if the active part has changed or if we are no longer the active window |
| if (windowContext.getParent().getActiveChild() == windowContext && part == activePart) { |
| // insert it in the beginning of the activation history, it may not have been inserted |
| // pending when this service was instantiated |
| partActivationHistory.prepend(part); |
| UIEvents.publishEvent(UIEvents.UILifeCycle.ACTIVATE, part); |
| return; |
| } |
| if (contextService != null) { |
| contextService.deferUpdates(true); |
| } |
| |
| MPart lastActivePart = activePart; |
| activePart = part; |
| |
| if (constructed && lastActivePart != null && lastActivePart != activePart) { |
| firePartDeactivated(lastActivePart); |
| } |
| |
| try { |
| // record any sibling into the activation history if necessary, this will allow it to be |
| // reselected again in the future as it will be an activation candidate in the future, |
| // this |
| // prevents other unrendered elements from being selected arbitrarily which would cause |
| // unwanted bundle activation |
| recordStackActivation(part); |
| |
| delegateBringToTop(part); |
| window.getParent().setSelectedElement(window); |
| |
| partActivationHistory.activate(part, activateBranch); |
| |
| if (requiresFocus) { |
| IPresentationEngine pe = part.getContext().get(IPresentationEngine.class); |
| pe.focusGui(part); |
| } |
| |
| firePartActivated(part); |
| UIEvents.publishEvent(UIEvents.UILifeCycle.ACTIVATE, part); |
| } finally { |
| if (contextService != null) { |
| contextService.deferUpdates(false); |
| } |
| } |
| } |
| |
| private void delegateBringToTop(MPart part) { |
| modelService.bringToTop(part); |
| createElement(part); |
| } |
| |
| /** |
| * Records the specified parent part's selected element in the activation history if the parent |
| * is a stack. |
| * |
| * @param part |
| * the part whose parent's selected element should be checked for activation history |
| * recording |
| */ |
| private void recordStackActivation(MPart part) { |
| MElementContainer<? extends MUIElement> parent = part.getParent(); |
| if (parent instanceof MGenericStack) { |
| recordSelectedActivation(parent); |
| } else if (parent == null) { |
| MPlaceholder placeholder = part.getCurSharedRef(); |
| if (placeholder != null) { |
| parent = placeholder.getParent(); |
| if (parent instanceof MGenericStack) { |
| recordSelectedActivation(parent); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Records the specified parent 's selected element in the activation history. |
| * |
| * @param parent |
| * the element whose selected element should be checked for activation history |
| * recording |
| */ |
| private void recordSelectedActivation(MElementContainer<? extends MUIElement> parent) { |
| MUIElement selectedElement = parent.getSelectedElement(); |
| if (selectedElement instanceof MPart) { |
| partActivationHistory.append((MPart) selectedElement); |
| } else if (selectedElement instanceof MPlaceholder) { |
| MUIElement ref = ((MPlaceholder) selectedElement).getRef(); |
| if (ref instanceof MPart) { |
| partActivationHistory.append((MPart) ref); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.e4.ui.workbench.modeling.EPartService#getActivePart() |
| */ |
| public MPart getActivePart() { |
| return activePart; |
| } |
| |
| private MPart createPart(MPartDescriptor descriptor) { |
| if (descriptor == null) { |
| return null; |
| } |
| |
| MPart part = BasicFactoryImpl.eINSTANCE.createPart(); |
| 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.getBindingContexts().addAll(descriptor.getBindingContexts()); |
| return part; |
| } |
| |
| public MPart createPart(String id) { |
| MPartDescriptor descriptor = modelService.getPartDescriptor(id); |
| return createPart(descriptor); |
| } |
| |
| public MPlaceholder createSharedPart(String id) { |
| return createSharedPart(id, false); |
| } |
| |
| public MPlaceholder createSharedPart(String id, boolean force) { |
| MWindow sharedWindow = getWindow(); |
| // Do we already have the part to share? |
| MPart sharedPart = null; |
| |
| // check for existing parts if necessary |
| if (!force) { |
| for (MUIElement element : sharedWindow.getSharedElements()) { |
| if (element.getElementId().equals(id)) { |
| sharedPart = (MPart) element; |
| break; |
| } |
| } |
| } |
| |
| if (sharedPart == null) { |
| MPartDescriptor descriptor = modelService.getPartDescriptor(id); |
| sharedPart = createPart(descriptor); |
| if (sharedPart == null) { |
| return null; |
| } |
| |
| // Replace the id to ensure that multi-instance parts work correctly |
| sharedPart.setElementId(id); |
| |
| sharedWindow.getSharedElements().add(sharedPart); |
| } |
| |
| return createSharedPart(sharedPart); |
| } |
| |
| private MPlaceholder createSharedPart(MPart sharedPart) { |
| // Create and return a reference to the shared part |
| MPlaceholder sharedPartRef = AdvancedFactoryImpl.eINSTANCE.createPlaceholder(); |
| sharedPartRef.setElementId(sharedPart.getElementId()); |
| sharedPartRef.setRef(sharedPart); |
| return sharedPartRef; |
| } |
| |
| /** |
| * Adds a part to the current container if it isn't already in the container. The part may still |
| * be added to the container if the part supports having multiple copies of itself in a given |
| * container. |
| * |
| * @param providedPart |
| * the part to add |
| * @param localPart |
| * a part that shares attributes with <code>providedPart</code>, for example, it may |
| * have been backed by the same part descriptor, this part may already be in the |
| * current container |
| * @return a part that has been added to the current container, note that this may not |
| * necessarily be <code>providedPart</code> |
| * @see MPartDescriptor#isAllowMultiple() |
| */ |
| private MPart addPart(MPart providedPart, MPart localPart) { |
| // If this is a multi-instance view see if there's a placeholder |
| String partId = providedPart.getElementId(); |
| int colonIndex = partId == null ? -1 : partId.indexOf(':'); |
| if (colonIndex >= 0) { |
| String descId = providedPart.getElementId().substring(0, colonIndex); |
| descId += ":*"; //$NON-NLS-1$ |
| List<MPlaceholder> phList = modelService.findElements(workbenchWindow, descId, |
| MPlaceholder.class, null); |
| if (phList.size() > 0) { |
| MUIElement phParent = phList.get(0).getParent(); |
| if (phParent instanceof MPartStack) { |
| MPartStack theStack = (MPartStack) phParent; |
| adjustPlaceholder(providedPart); |
| MPlaceholder placeholder = providedPart.getCurSharedRef(); |
| if (placeholder == null) { |
| theStack.getChildren().add(providedPart); |
| } else { |
| theStack.getChildren().add(placeholder); |
| } |
| |
| } |
| } |
| } |
| MPartDescriptor descriptor = modelService.getPartDescriptor(providedPart.getElementId()); |
| if (descriptor == null) { |
| // there is no part descriptor backing the provided part, just add it to the container |
| // if it's not already there |
| if (!isInContainer(providedPart)) { |
| adjustPlaceholder(providedPart); |
| addToLastContainer(null, providedPart); |
| } |
| } else { |
| if (providedPart != localPart && !descriptor.isAllowMultiple()) { |
| // multiple copies of this part are not allowed, just return the local one |
| return localPart; |
| } |
| |
| // already in the container, return as is |
| if (isInContainer(providedPart)) { |
| return providedPart; |
| } |
| |
| // corrects this part's placeholder if necessary |
| adjustPlaceholder(providedPart); |
| |
| String category = descriptor.getCategory(); |
| if (category == null) { |
| // no category, just add it to the end |
| addToLastContainer(null, providedPart); |
| } else { |
| if ("org.eclipse.e4.primaryDataStack".equals(category)) { //$NON-NLS-1$ |
| MElementContainer<MUIElement> container = getContainer(); |
| MUIElement area = modelService.find("org.eclipse.ui.editorss", container); //$NON-NLS-1$ |
| |
| MPartStack activeStack = null; |
| if (area instanceof MPlaceholder |
| && ((MPlaceholder) area).getRef() instanceof MArea) { |
| // Find the currently 'active' stack in the area |
| MArea a = (MArea) ((MPlaceholder) area).getRef(); |
| MUIElement curActive = a.getSelectedElement(); |
| while (curActive instanceof MElementContainer<?>) { |
| if (curActive instanceof MPartStack) { |
| activeStack = (MPartStack) curActive; |
| break; |
| } |
| MElementContainer<?> curContainer = (MElementContainer<?>) curActive; |
| curActive = curContainer.getSelectedElement(); |
| } |
| } |
| |
| if (activeStack != null) { |
| activeStack.getChildren().add(providedPart); |
| } else { |
| // Find the first visible stack in the area |
| List<MPartStack> sharedStacks = modelService.findElements(area, null, |
| MPartStack.class, null); |
| if (sharedStacks.size() > 0) { |
| for (MPartStack stack : sharedStacks) { |
| if (stack.isToBeRendered()) { |
| stack.getChildren().add(providedPart); |
| break; |
| } |
| } |
| } else { |
| addToLastContainer(null, providedPart); |
| } |
| } |
| } else { |
| List<MElementContainer> containers = modelService.findElements(getContainer(), |
| null, MElementContainer.class, Collections.singletonList(category)); |
| if (containers.isEmpty()) { |
| // couldn't find any containers with the specified tag, just add it to the |
| // end |
| addToLastContainer(category, providedPart); |
| } else { |
| // add the part to the container |
| MElementContainer container = containers.get(0); |
| MPlaceholder placeholder = providedPart.getCurSharedRef(); |
| if (placeholder == null) { |
| container.getChildren().add(providedPart); |
| } else { |
| container.getChildren().add(placeholder); |
| } |
| } |
| } |
| } |
| } |
| return providedPart; |
| } |
| |
| private void adjustPlaceholder(MPart part) { |
| if (isShared(part)) { |
| MPlaceholder placeholder = part.getCurSharedRef(); |
| // if this part doesn't have any placeholders, we need to make one |
| if (placeholder == null |
| // alternatively, if it has one but it's not in the current container, then we |
| // need to spawn another one as we don't want to reuse the same one and end up |
| // shifting that placeholder to the current container during the add operation |
| || (placeholder.getParent() != null && !isInContainer(placeholder))) { |
| placeholder = createSharedPart(part); |
| part.setCurSharedRef(placeholder); |
| } |
| } |
| } |
| |
| private boolean isShared(MPart part) { |
| return getWindow().getSharedElements().contains(part); |
| } |
| |
| private void addToLastContainer(String category, MPart part) { |
| MElementContainer<?> lastContainer = getLastContainer(); |
| MPlaceholder placeholder = part.getCurSharedRef(); |
| if (placeholder == null) { |
| ((List) lastContainer.getChildren()).add(part); |
| } else { |
| ((List) lastContainer.getChildren()).add(placeholder); |
| } |
| |
| if (category != null) { |
| lastContainer.getTags().add(category); |
| } |
| } |
| |
| private MElementContainer<?> getLastContainer() { |
| MElementContainer<MUIElement> searchRoot = getContainer(); |
| List<MUIElement> children = searchRoot.getChildren(); |
| if (children.size() == 0) { |
| MPartStack stack = BasicFactoryImpl.eINSTANCE.createPartStack(); |
| searchRoot.getChildren().add(stack); |
| return stack; |
| } |
| |
| MElementContainer<?> lastContainer = getLastContainer(searchRoot, children); |
| if (lastContainer == null) { |
| MPartStack stack = BasicFactoryImpl.eINSTANCE.createPartStack(); |
| searchRoot.getChildren().add(stack); |
| return stack; |
| } else if (!(lastContainer instanceof MPartStack)) { |
| MPartStack stack = BasicFactoryImpl.eINSTANCE.createPartStack(); |
| ((List) lastContainer.getChildren()).add(stack); |
| return stack; |
| } |
| return lastContainer; |
| } |
| |
| private MElementContainer<?> getLastContainer(MElementContainer<?> container, List<?> children) { |
| if (children.isEmpty()) { |
| return null; |
| } |
| |
| for (int i = children.size() - 1; i > -1; i--) { |
| Object muiElement = children.get(i); |
| if (muiElement instanceof MElementContainer<?>) { |
| MElementContainer<?> childContainer = (MElementContainer<?>) muiElement; |
| MElementContainer<?> lastContainer = getLastContainer(childContainer, |
| childContainer.getChildren()); |
| if (lastContainer != null) { |
| return lastContainer; |
| } |
| } |
| } |
| return container; |
| } |
| |
| /** |
| * Returns the parent container of the specified element. If one cannot be found, a check will |
| * be performed to see whether the element is being represented by a placeholder, if it is, the |
| * placeholder's parent will be returned, if any. |
| * |
| * @param element |
| * the element to query |
| * @return the element's parent container, or the parent container of the specified element's |
| * current placeholder, if it has one |
| */ |
| private MElementContainer<MUIElement> getParent(MUIElement element) { |
| MElementContainer<MUIElement> parent = element.getParent(); |
| if (parent == null) { |
| MPlaceholder placeholder = element.getCurSharedRef(); |
| if (placeholder == null) { |
| MElementContainer<MUIElement> container = getContainer(); |
| return findContainer(container, element); |
| } |
| return placeholder.getParent(); |
| } |
| return parent; |
| } |
| |
| private MElementContainer<MUIElement> findContainer(MElementContainer<?> container, |
| MUIElement element) { |
| for (Object child : container.getChildren()) { |
| if (child == element) { |
| return (MElementContainer<MUIElement>) container; |
| } else if (child instanceof MPlaceholder) { |
| MPlaceholder placeholder = (MPlaceholder) child; |
| MUIElement ref = placeholder.getRef(); |
| if (ref == element) { |
| return (MElementContainer<MUIElement>) container; |
| } else if (ref instanceof MElementContainer<?>) { |
| MElementContainer<MUIElement> match = findContainer((MElementContainer<?>) ref, |
| element); |
| if (match != null) { |
| return match; |
| } |
| } |
| } else if (child instanceof MElementContainer<?>) { |
| MElementContainer<MUIElement> match = findContainer((MElementContainer<?>) child, |
| element); |
| if (match != null) { |
| return match; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private MUIElement getRemoveTarget(MPart part) { |
| MPlaceholder placeholder = getLocalPlaceholder(part); |
| return placeholder == null ? part : placeholder; |
| } |
| |
| public MPart addPart(MPart part) { |
| Assert.isNotNull(part); |
| MPart localPart = findPart(part.getElementId()); |
| return addPart(part, localPart == null ? part : localPart); |
| } |
| |
| public MPart showPart(String id, PartState partState) { |
| Assert.isNotNull(id); |
| Assert.isNotNull(partState); |
| |
| MPart part = findPart(id); |
| if (part == null) { |
| MPartDescriptor descriptor = modelService.getPartDescriptor(id); |
| part = createPart(descriptor); |
| if (part == null) { |
| return null; |
| } |
| } |
| return showPart(addPart(part), partState); |
| } |
| |
| public MPart showPart(MPart part, PartState partState) { |
| Assert.isNotNull(part); |
| Assert.isNotNull(partState); |
| |
| MPart addedPart = addPart(part); |
| MPlaceholder localPlaceholder = getLocalPlaceholder(addedPart); |
| // correct the placeholder setting if necessary |
| if (localPlaceholder != null && addedPart.getCurSharedRef() != localPlaceholder) { |
| addedPart.setCurSharedRef(localPlaceholder); |
| } |
| |
| switch (partState) { |
| case ACTIVATE: |
| activate(addedPart); |
| return addedPart; |
| case VISIBLE: |
| MPart activePart = getActivePart(); |
| if (activePart == null || getParent(activePart) == getParent(addedPart)) { |
| activate(addedPart); |
| } else { |
| bringToTop(addedPart); |
| } |
| return addedPart; |
| case CREATE: |
| createElement(addedPart); |
| return addedPart; |
| } |
| return addedPart; |
| } |
| |
| private void createElement(MUIElement element) { |
| if (modelService.isHostedElement(element, workbenchWindow)) { |
| // assume the client has full control |
| return; |
| } |
| |
| MPlaceholder placeholder = element.getCurSharedRef(); |
| if (placeholder != null) { |
| element.setToBeRendered(true); |
| element = placeholder; |
| } |
| |
| // render this element |
| element.setToBeRendered(true); |
| |
| // render all of its parents |
| MUIElement parentWindow = workbenchWindow; |
| // determine the top parent that needs to be forcibly created |
| MUIElement target = null; |
| MElementContainer<MUIElement> parent = element.getParent(); |
| while (parent != null && parent != parentWindow) { |
| parent.setToBeRendered(true); |
| if (parent.getWidget() == null) { |
| target = parent; |
| } |
| parent = parent.getParent(); |
| } |
| if (target != null) { |
| // force the element's parent hierarchy to be created |
| engine.createGui(target); |
| } |
| // ask the engine to create the element |
| engine.createGui(element); |
| |
| parent = element.getParent(); |
| if (parent != null && parent.getChildren().size() == 1) { |
| // if we're the only child, set ourselves as the selected element |
| parent.setSelectedElement(element); |
| } |
| } |
| |
| public void requestActivation() { |
| if (activePart == null) { |
| MPart candidate = partActivationHistory.getActivationCandidate(getParts()); |
| if (candidate != null) { |
| activate(candidate); |
| } |
| } else if (!partActivationHistory.isValid(activePart) || !getParts().contains(activePart)) { |
| MPart candidate = partActivationHistory.getNextActivationCandidate(getParts(), |
| activePart); |
| if (candidate != null) { |
| activate(candidate); |
| } |
| } |
| } |
| |
| public void hidePart(MPart part) { |
| hidePart(part, false); |
| } |
| |
| public void hidePart(MPart part, boolean force) { |
| if (isInContainer(part)) { |
| MPlaceholder sharedRef = part.getCurSharedRef(); |
| MUIElement toBeRemoved = getRemoveTarget(part); |
| MElementContainer<MUIElement> parent = getParent(toBeRemoved); |
| List<MUIElement> children = parent.getChildren(); |
| |
| // check if we're a placeholder but not actually the shared ref of the part |
| if (toBeRemoved != part && toBeRemoved instanceof MPlaceholder |
| && sharedRef != toBeRemoved) { |
| toBeRemoved.setToBeRendered(false); |
| |
| // if so, not much to do, remove ourselves if necessary but that's it |
| if (force || part.getTags().contains(REMOVE_ON_HIDE_TAG)) { |
| parent.getChildren().remove(toBeRemoved); |
| } |
| return; |
| } |
| |
| boolean isActiveChild = isActiveChild(part); |
| MPart activationCandidate = null; |
| // check if we're the active child |
| if (isActiveChild) { |
| // get the activation candidate if we are |
| activationCandidate = partActivationHistory.getNextActivationCandidate(getParts(), |
| part); |
| } |
| |
| MPerspective thePersp = modelService.getPerspectiveFor(toBeRemoved); |
| boolean needNewSel = thePersp == null || !thePersp.getTags().contains("PerspClosing"); //$NON-NLS-1$ |
| if (needNewSel) { |
| if (parent.getSelectedElement() == toBeRemoved) { |
| // if we're the selected element and we're going to be hidden, need to select |
| // something else |
| MUIElement candidate = partActivationHistory.getSiblingSelectionCandidate(part); |
| candidate = candidate == null ? null |
| : candidate.getCurSharedRef() == null ? candidate : candidate |
| .getCurSharedRef(); |
| if (candidate != null && children.contains(candidate)) { |
| parent.setSelectedElement(candidate); |
| } else { |
| for (MUIElement child : children) { |
| if (child != toBeRemoved && child.isToBeRendered()) { |
| parent.setSelectedElement(child); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (activationCandidate == null) { |
| // nothing else to activate and we're the active child, deactivate |
| if (isActiveChild) { |
| part.getContext().deactivate(); |
| } |
| } else { |
| // activate our candidate |
| activate(activationCandidate); |
| } |
| } |
| |
| if (toBeRemoved != null) { |
| toBeRemoved.setToBeRendered(false); |
| } else { |
| part.setToBeRendered(false); |
| } |
| |
| if (parent.getSelectedElement() == toBeRemoved) { |
| parent.setSelectedElement(null); |
| } |
| |
| if (force || part.getTags().contains(REMOVE_ON_HIDE_TAG)) { |
| children.remove(toBeRemoved); |
| } |
| // remove ourselves from the activation history also since we're being hidden |
| partActivationHistory.forget(getWindow(), part, toBeRemoved == part); |
| } |
| } |
| |
| private boolean isActiveChild(MPart part) { |
| IEclipseContext context = part.getContext(); |
| return context != null && context.getParent().getActiveChild() == context; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.e4.ui.workbench.modeling.EPartService#getDirtyParts() |
| */ |
| public Collection<MPart> getDirtyParts() { |
| List<MPart> dirtyParts = new ArrayList<MPart>(); |
| for (MPart part : getParts()) { |
| if (part.isDirty()) { |
| dirtyParts.add(part); |
| } |
| } |
| return dirtyParts; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.e4.ui.workbench.modeling.EPartService#save(org.eclipse.e4.ui.model.application. |
| * MSaveablePart, boolean) |
| */ |
| public boolean savePart(MPart part, boolean confirm) { |
| if (!part.isDirty()) { |
| return true; |
| } |
| |
| if (saveHandler != null) { |
| return saveHandler.save(part, confirm); |
| } |
| |
| Object client = part.getObject(); |
| try { |
| ContextInjectionFactory.invoke(client, Persist.class, part.getContext()); |
| } catch (InjectionException e) { |
| log("Failed to persist contents of part", "Failed to persist contents of part ({0})", //$NON-NLS-1$ //$NON-NLS-2$ |
| part.getElementId(), e); |
| return false; |
| } catch (RuntimeException e) { |
| log("Failed to persist contents of part via DI", //$NON-NLS-1$ |
| "Failed to persist contents of part ({0}) via DI", part.getElementId(), e); //$NON-NLS-1$ |
| return false; |
| } |
| return true; |
| } |
| |
| public boolean saveAll(boolean confirm) { |
| Collection<MPart> dirtyParts = getDirtyParts(); |
| if (dirtyParts.isEmpty()) { |
| return true; |
| } |
| if (saveHandler != null) { |
| return saveHandler.saveParts(dirtyParts, confirm); |
| } |
| |
| for (MPart dirtyPart : dirtyParts) { |
| if (!savePart(dirtyPart, false)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public Collection<MInputPart> getInputParts(String inputUri) { |
| Assert.isNotNull(inputUri, "Input uri must not be null"); //$NON-NLS-1$ |
| |
| Collection<MInputPart> rv = new ArrayList<MInputPart>(); |
| |
| for (MInputPart p : getParts(MInputPart.class, null)) { |
| if (inputUri.equals(p.getInputURI())) { |
| rv.add(p); |
| } |
| } |
| |
| return rv; |
| } |
| |
| /** |
| * "Container" here is: 1) a selected MPerspective, or, if none available 2) the MWindow for |
| * which this part service is created, or, if not available, 3) the MApplication. |
| */ |
| private MElementContainer<MUIElement> getContainer() { |
| MElementContainer<? extends MUIElement> outerContainer = (workbenchWindow != null) ? workbenchWindow |
| : application; |
| |
| // see if we can narrow it down to the active perspective |
| for (MElementContainer<?> container = outerContainer; container != null;) { |
| if (container instanceof MPerspective) |
| return (MElementContainer<MUIElement>) container; |
| Object child = container.getSelectedElement(); |
| if (child == null) |
| break; |
| if (child instanceof MElementContainer<?>) |
| container = (MElementContainer<?>) child; |
| else |
| break; |
| } |
| return (MElementContainer<MUIElement>) outerContainer; |
| } |
| } |