| /******************************************************************************* |
| * Copyright (c) 2009, 2010 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.workbench.ui.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.e4.core.contexts.ContextInjectionFactory; |
| import org.eclipse.e4.core.contexts.IContextConstants; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.contexts.RunAndTrack; |
| import org.eclipse.e4.core.di.InjectionException; |
| import org.eclipse.e4.core.di.annotations.Optional; |
| import org.eclipse.e4.core.di.annotations.PostConstruct; |
| import org.eclipse.e4.core.di.annotations.PreDestroy; |
| import org.eclipse.e4.core.services.events.IEventBroker; |
| import org.eclipse.e4.core.services.log.Logger; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.MApplicationElement; |
| 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.MUIElement; |
| 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.services.IServiceConstants; |
| import org.eclipse.e4.workbench.modeling.EModelService; |
| import org.eclipse.e4.workbench.modeling.EPartService; |
| import org.eclipse.e4.workbench.modeling.IPartListener; |
| import org.eclipse.e4.workbench.modeling.ISaveHandler; |
| import org.eclipse.e4.workbench.modeling.ISaveHandler.Save; |
| import org.eclipse.e4.workbench.ui.IPresentationEngine; |
| import org.eclipse.e4.workbench.ui.Persist; |
| import org.eclipse.e4.workbench.ui.UIEvents; |
| 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); |
| Object selected = event.getProperty(UIEvents.EventTags.NEW_VALUE); |
| |
| MPart oldSelectedPart = oldSelected instanceof MPart ? (MPart) oldSelected : null; |
| MPart selectedPart = selected instanceof MPart ? (MPart) selected : null; |
| |
| if (oldSelectedPart != null && isInContainer(oldSelectedPart)) { |
| firePartHidden(oldSelectedPart); |
| } |
| |
| if (selectedPart != null && selectedPart.isToBeRendered() |
| && isInContainer(selectedPart)) { |
| firePartVisible(selectedPart); |
| firePartBroughtToTop(selectedPart); |
| } |
| } |
| } |
| }; |
| |
| @Inject |
| private MApplication application; |
| |
| /** |
| * This is the specific implementation. TODO: generalize it |
| */ |
| @Inject |
| @Named(EPartService.PART_SERVICE_ROOT) |
| private MElementContainer<MUIElement> rootContainer; |
| |
| @Inject |
| private IPresentationEngine engine; |
| |
| @Inject |
| private EModelService modelService; |
| |
| @Inject |
| private Logger logger; |
| |
| @Inject |
| @Optional |
| private ISaveHandler saveHandler; |
| |
| @Inject |
| private IEventBroker eventBroker; |
| |
| private MPart activePart; |
| |
| private MPart lastActivePart; |
| |
| private ListenerList listeners = new ListenerList(); |
| |
| private boolean constructed = false; |
| |
| public PartServiceImpl() { |
| // placeholder |
| } |
| |
| @Inject |
| void setPart(@Optional @Named(IServiceConstants.ACTIVE_PART) MPart p) { |
| if (activePart != p) { |
| lastActivePart = activePart; |
| activePart = p; |
| |
| // no need to do anything if we have no listeners |
| if (constructed && !listeners.isEmpty()) { |
| if (lastActivePart != null && lastActivePart != activePart) { |
| firePartDeactivated(lastActivePart); |
| } |
| |
| if (activePart != null) { |
| firePartActivated(activePart); |
| } |
| } |
| } |
| } |
| |
| @PostConstruct |
| void postConstruct() { |
| eventBroker.subscribe(UIEvents.buildTopic(UIEvents.ElementContainer.TOPIC, |
| UIEvents.ElementContainer.SELECTEDELEMENT), selectedHandler); |
| constructed = true; |
| |
| if (rootContainer == null) { |
| // couldn't find one, we'll just track the application then, it is |
| // questionable why someone would ask the application for the part |
| // service though |
| application.getContext().runAndTrack(new RunAndTrack() { |
| public boolean changed(IEclipseContext eventsContext) { |
| IEclipseContext childContext = (IEclipseContext) eventsContext |
| .getLocal(IContextConstants.ACTIVE_CHILD); |
| if (childContext != null) { |
| rootContainer = (MElementContainer<MUIElement>) childContext |
| .get(MWindow.class.getName()); |
| } |
| return true; |
| } |
| }); |
| } |
| } |
| |
| @PreDestroy |
| void preDestroy() { |
| constructed = false; |
| eventBroker.unsubscribe(selectedHandler); |
| } |
| |
| private void firePartActivated(MPart part) { |
| for (Object listener : listeners.getListeners()) { |
| ((IPartListener) listener).partActivated(part); |
| } |
| } |
| |
| private void firePartDeactivated(MPart part) { |
| for (Object listener : listeners.getListeners()) { |
| ((IPartListener) listener).partDeactivated(part); |
| } |
| } |
| |
| private void firePartHidden(MPart part) { |
| for (Object listener : listeners.getListeners()) { |
| ((IPartListener) listener).partHidden(part); |
| } |
| } |
| |
| private void firePartVisible(MPart part) { |
| for (Object listener : listeners.getListeners()) { |
| ((IPartListener) listener).partVisible(part); |
| } |
| } |
| |
| private void firePartBroughtToTop(MPart part) { |
| for (Object listener : listeners.getListeners()) { |
| ((IPartListener) listener).partBroughtToTop(part); |
| } |
| } |
| |
| public void addPartListener(IPartListener listener) { |
| listeners.add(listener); |
| } |
| |
| public void removePartListener(IPartListener listener) { |
| listeners.remove(listener); |
| } |
| |
| private MContext getParentWithContext(MUIElement part) { |
| MElementContainer<MUIElement> parent = part.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)) { |
| part.setToBeRendered(true); |
| internalBringToTop(part); |
| } |
| } |
| |
| private void internalBringToTop(MPart part) { |
| MElementContainer<MUIElement> parent = part.getParent(); |
| MPart oldSelectedElement = (MPart) parent.getSelectedElement(); |
| if (oldSelectedElement != part) { |
| parent.setSelectedElement(part); |
| internalFixContext(part, oldSelectedElement); |
| } |
| } |
| |
| private void internalFixContext(MPart part, MPart oldSelectedElement) { |
| MContext parentPart = oldSelectedElement == null ? null |
| : getParentWithContext(oldSelectedElement); |
| if (parentPart == null) { |
| return; |
| } |
| IEclipseContext parentContext = parentPart.getContext(); |
| IEclipseContext oldContext = oldSelectedElement.getContext(); |
| Object child = parentContext.get(IContextConstants.ACTIVE_CHILD); |
| if (child == oldContext) { |
| parentContext.set(IContextConstants.ACTIVE_CHILD, |
| part == null ? null : part.getContext()); |
| } |
| } |
| |
| public MPart findPart(String id) { |
| MApplicationElement element = modelService.find(id, rootContainer); |
| return element instanceof MPart ? (MPart) element : null; |
| } |
| |
| public Collection<MPart> getParts() { |
| return modelService.findElements(rootContainer, null, MPart.class, null); |
| } |
| |
| public boolean isPartVisible(MPart part) { |
| if (isInContainer(part)) { |
| MElementContainer<?> parent = part.getParent(); |
| if (parent instanceof MPartStack) { |
| return parent.getSelectedElement() == part; |
| } |
| |
| return part.isVisible(); |
| } |
| return false; |
| } |
| |
| private boolean isInContainer(MPart part) { |
| MUIElement p = modelService.find(part.getElementId(), rootContainer); |
| if (p != null) |
| return true; |
| return isInContainer(rootContainer, part); |
| } |
| |
| private boolean isInContainer(MElementContainer<?> container, MPart part) { |
| for (Object object : container.getChildren()) { |
| if (object == part) { |
| return true; |
| } else if (object instanceof MElementContainer<?>) { |
| if (isInContainer((MElementContainer<?>) object, part)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.e4.workbench.modeling.EPartService#activate(org.eclipse.e4.ui.model.application |
| * .MPart) |
| */ |
| public void activate(MPart part) { |
| if (part == activePart || !isInContainer(part)) { |
| return; |
| } |
| |
| modelService.bringToTop(window, part); |
| IEclipseContext context = part.getContext(); |
| IEclipseContext parent = context.getParent(); |
| while (parent != null) { |
| parent.set(IContextConstants.ACTIVE_CHILD, context); |
| context = parent; |
| parent = parent.getParent(); |
| } |
| } |
| |
| @Inject |
| @Optional |
| MWindow window; |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.e4.workbench.modeling.EPartService#getActivePart() |
| */ |
| public MPart getActivePart() { |
| return activePart; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.e4.workbench.modeling.EPartService#deactivate(org.eclipse.e4.ui.model.application |
| * .MPart) |
| */ |
| public void deactivate(MPart part) { |
| MElementContainer<MUIElement> parent = part.getParent(); |
| MPart oldSelectedElement = (MPart) parent.getSelectedElement(); |
| if (oldSelectedElement == part) { |
| parent.setSelectedElement(null); |
| internalFixContext(null, oldSelectedElement); |
| } |
| } |
| |
| private MPartDescriptor findDescriptor(String id) { |
| for (MPartDescriptor descriptor : application.getDescriptors()) { |
| if (descriptor.getElementId().equals(id)) { |
| return descriptor; |
| } |
| } |
| return null; |
| } |
| |
| private MPart createPart(MPartDescriptor descriptor) { |
| if (descriptor == null) { |
| return null; |
| } |
| |
| MPart part = BasicFactoryImpl.eINSTANCE.createPart(); |
| part.setElementId(descriptor.getElementId()); |
| part.getMenus().addAll(descriptor.getMenus()); |
| part.setToolbar(descriptor.getToolbar()); |
| part.setCloseable(descriptor.isCloseable()); |
| part.setContributionURI(descriptor.getContributionURI()); |
| part.setLabel(descriptor.getLabel()); |
| part.setIconURI(descriptor.getIconURI()); |
| part.setTooltip(descriptor.getTooltip()); |
| part.getHandlers().addAll(descriptor.getHandlers()); |
| part.getTags().addAll(descriptor.getTags()); |
| part.getBindingContexts().addAll(descriptor.getBindingContexts()); |
| return part; |
| } |
| |
| public MPart createPart(String id) { |
| MPartDescriptor descriptor = findDescriptor(id); |
| return createPart(descriptor); |
| } |
| |
| private MPart addPart(MPart providedPart, MPart localPart) { |
| if (providedPart == localPart && isInContainer(providedPart)) { |
| return providedPart; |
| } |
| |
| MPartDescriptor descriptor = findDescriptor(providedPart.getElementId()); |
| if (descriptor == null) { |
| if (providedPart != localPart) { |
| MPartStack stack = BasicFactoryImpl.eINSTANCE.createPartStack(); |
| stack.getChildren().add(providedPart); |
| rootContainer.getChildren().add(stack); |
| } |
| } else { |
| if (providedPart != localPart && !descriptor.isAllowMultiple()) { |
| return localPart; |
| } |
| |
| String category = descriptor.getCategory(); |
| if (category == null) { |
| addToLastContainer(null, providedPart); |
| } else { |
| List<Object> elements = modelService.findElements(rootContainer, null, null, |
| Collections.singletonList(category)); |
| if (elements.isEmpty()) { |
| addToLastContainer(category, providedPart); |
| } else { |
| Object element = elements.get(0); |
| if (element instanceof MElementContainer<?>) { |
| ((MElementContainer<MPart>) element).getChildren().add(providedPart); |
| } else { |
| addToLastContainer(category, providedPart); |
| } |
| } |
| } |
| } |
| return providedPart; |
| } |
| |
| private void addToLastContainer(String category, MPart part) { |
| MElementContainer<?> lastContainer = getLastContainer(); |
| ((List) lastContainer.getChildren()).add(part); |
| if (category != null) { |
| lastContainer.getTags().add(category); |
| } |
| } |
| |
| private MElementContainer<?> getLastContainer() { |
| MElementContainer<MUIElement> searchRoot = rootContainer; |
| 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; |
| } |
| 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; |
| } |
| |
| private MPart showPart(PartState partState, MPart providedPart, MPart localPart) { |
| MPart part = addPart(providedPart, localPart); |
| switch (partState) { |
| case ACTIVATE: |
| activate(part); |
| return part; |
| case VISIBLE: |
| MPart activePart = getActivePart(); |
| if (activePart == null) { |
| bringToTop(part); |
| } else if (activePart.getParent() == part.getParent()) { |
| // same parent as the active part, just instantiate this part then |
| part.setToBeRendered(true); |
| engine.createGui(part); |
| } else { |
| bringToTop(part); |
| } |
| return part; |
| case CREATE: |
| part.setToBeRendered(true); |
| engine.createGui(part); |
| return part; |
| } |
| return part; |
| } |
| |
| public MPart showPart(String id, PartState partState) { |
| Assert.isNotNull(id); |
| Assert.isNotNull(partState); |
| |
| MPart part = findPart(id); |
| if (part != null) { |
| return showPart(part, partState); |
| } |
| |
| MPartDescriptor descriptor = findDescriptor(id); |
| part = createPart(descriptor); |
| if (part == null) { |
| return null; |
| } |
| |
| return showPart(partState, part, part); |
| } |
| |
| public MPart showPart(MPart part, PartState partState) { |
| Assert.isNotNull(part); |
| Assert.isNotNull(partState); |
| |
| MPart localPart = findPart(part.getElementId()); |
| if (localPart != null) { |
| return showPart(partState, part, localPart); |
| } |
| return showPart(partState, part, part); |
| } |
| |
| public void hidePart(MPart part) { |
| if (isInContainer(part)) { |
| part.setToBeRendered(false); |
| |
| if (part.getTags().contains(REMOVE_ON_HIDE_TAG)) { |
| MElementContainer<MUIElement> parent = part.getParent(); |
| List<MUIElement> children = parent.getChildren(); |
| children.remove(part); |
| |
| // FIXME: should be based on activation list |
| if (parent.getSelectedElement() == part && !children.isEmpty()) { |
| parent.setSelectedElement(children.get(0)); |
| } |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.e4.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.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 (confirm && saveHandler != null) { |
| switch (saveHandler.promptToSave(part)) { |
| case NO: |
| return true; |
| case CANCEL: |
| return false; |
| } |
| } |
| |
| Object client = part.getObject(); |
| try { |
| ContextInjectionFactory.invoke(client, Persist.class, part.getContext()); |
| } catch (InjectionException e) { |
| Throwable throwable = e.getCause(); |
| if (throwable == null) { |
| logger.error(e.getMessage()); |
| } else { |
| logger.error(throwable); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| public boolean saveAll(boolean confirm) { |
| Collection<MPart> dirtyParts = getDirtyParts(); |
| if (dirtyParts.isEmpty()) { |
| return true; |
| } |
| |
| if (confirm && saveHandler != null) { |
| List<MPart> dirtyPartsList = Collections.unmodifiableList(new ArrayList<MPart>( |
| dirtyParts)); |
| Save[] decisions = saveHandler.promptToSave(dirtyPartsList); |
| for (Save decision : decisions) { |
| if (decision == Save.CANCEL) { |
| return false; |
| } |
| } |
| |
| for (int i = 0; i < decisions.length; i++) { |
| if (decisions[i] == Save.YES) { |
| if (!savePart(dirtyPartsList.get(i), false)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| for (MPart dirtyPart : dirtyParts) { |
| if (!savePart(dirtyPart, false)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private Collection<MInputPart> getInputParts() { |
| return modelService.findElements(rootContainer, null, MInputPart.class, null); |
| } |
| |
| 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 : getInputParts()) { |
| if (inputUri.equals(p.getInputURI())) { |
| rv.add(p); |
| } |
| } |
| |
| return rv; |
| } |
| } |