| /******************************************************************************* |
| * Copyright (c) 2011 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.workbench.addons.cleanupaddon; |
| |
| import javax.annotation.PostConstruct; |
| import javax.annotation.PreDestroy; |
| import javax.inject.Inject; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.services.events.IEventBroker; |
| import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.ui.MElementContainer; |
| import org.eclipse.e4.ui.model.application.ui.MUIElement; |
| import org.eclipse.e4.ui.model.application.ui.advanced.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.basic.MPartSashContainer; |
| import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar; |
| import org.eclipse.e4.ui.model.application.ui.basic.MWindow; |
| import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; |
| import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; |
| import org.eclipse.e4.ui.workbench.UIEvents; |
| import org.eclipse.e4.ui.workbench.modeling.EModelService; |
| import org.eclipse.e4.ui.workbench.renderers.swt.SashLayout; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventHandler; |
| |
| public class CleanupAddon { |
| @Inject |
| IEventBroker eventBroker; |
| |
| @Inject |
| EModelService modelService; |
| |
| @Inject |
| MApplication app; |
| |
| private EventHandler childrenHandler = new EventHandler() { |
| public void handleEvent(Event event) { |
| Object changedObj = event.getProperty(UIEvents.EventTags.ELEMENT); |
| String eventType = (String) event.getProperty(UIEvents.EventTags.TYPE); |
| if (UIEvents.EventTypes.REMOVE.equals(eventType)) { |
| final MElementContainer<?> container = (MElementContainer<?>) changedObj; |
| MUIElement containerParent = container.getParent(); |
| |
| // Determine the elements that should *not* ever be auto-destroyed |
| if (container instanceof MApplication || container instanceof MPerspectiveStack |
| || container instanceof MMenuElement || container instanceof MTrimBar |
| || container instanceof MToolBar || container instanceof MArea) { |
| return; |
| } |
| |
| if (container instanceof MWindow && containerParent instanceof MApplication) { |
| return; |
| } |
| |
| Display display = Display.getCurrent(); |
| |
| // Stall the removal to handle cases where the container is only transiently empty |
| if (display != null) { |
| Display.getCurrent().asyncExec(new Runnable() { |
| public void run() { |
| // Remove it from the display if no visible children |
| int tbrCount = modelService.toBeRenderedCount(container); |
| |
| // Cache the value since setting the TBR may change the result |
| boolean lastStack = isLastEditorStack(container); |
| if (tbrCount == 0 && !lastStack) { |
| container.setToBeRendered(false); |
| } |
| |
| // Remove it from the model if it has no children at all |
| if (container.getChildren().size() == 0) { |
| MElementContainer<MUIElement> parent = container.getParent(); |
| if (parent != null && !lastStack) { |
| container.setToBeRendered(false); |
| parent.getChildren().remove(container); |
| } else if (container instanceof MWindow) { |
| // Must be a Detached Window |
| MUIElement eParent = (MUIElement) ((EObject) container) |
| .eContainer(); |
| if (eParent instanceof MPerspective) { |
| ((MPerspective) eParent).getWindows().remove(container); |
| } else if (eParent instanceof MWindow) { |
| ((MWindow) eParent).getWindows().remove(container); |
| } |
| } |
| } else if (container.getChildren().size() == 1 |
| && container instanceof MPartSashContainer) { |
| // if a sash container has only one element then remove it and move |
| // its child up to where it used to be |
| MUIElement theChild = container.getChildren().get(0); |
| MElementContainer<MUIElement> parentContainer = container |
| .getParent(); |
| if (parentContainer != null) { |
| int index = parentContainer.getChildren().indexOf(container); |
| |
| // Magic check, are we unwrapping a sash container |
| if (theChild instanceof MPartSashContainer) { |
| if (container.getWidget() instanceof Composite) { |
| Composite theComp = (Composite) container.getWidget(); |
| Object tmp = theChild.getWidget(); |
| theChild.setWidget(theComp); |
| theComp.setLayout(new SashLayout(theComp, theChild)); |
| theComp.setData(AbstractPartRenderer.OWNING_ME, |
| theChild); |
| container.setWidget(tmp); |
| } |
| } |
| |
| theChild.setContainerData(container.getContainerData()); |
| container.getChildren().remove(theChild); |
| parentContainer.getChildren().add(index, theChild); |
| container.setToBeRendered(false); |
| parentContainer.getChildren().remove(container); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| }; |
| |
| private EventHandler visibilityChangeHandler = new EventHandler() { |
| public void handleEvent(Event event) { |
| MUIElement changedObj = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT); |
| if (changedObj instanceof MTrimBar) |
| return; |
| |
| if (changedObj.getWidget() instanceof Shell) { |
| ((Shell) changedObj.getWidget()).setVisible(changedObj.isVisible()); |
| } else if (changedObj.getWidget() instanceof Rectangle) { |
| if (changedObj.isVisible()) { |
| // Make all the parents visible |
| MUIElement parent = changedObj.getParent(); |
| if (!parent.isVisible()) |
| parent.setVisible(true); |
| } else { |
| // If there are no more 'visible' children then make the parent go away too |
| MElementContainer<MUIElement> parent = changedObj.getParent(); |
| boolean makeInvisible = true; |
| for (MUIElement kid : parent.getChildren()) { |
| if (kid.isToBeRendered() && kid.isVisible()) { |
| makeInvisible = false; |
| break; |
| } |
| } |
| if (makeInvisible) |
| parent.setVisible(false); |
| } |
| } else if (changedObj.getWidget() instanceof Control) { |
| Control ctrl = (Control) changedObj.getWidget(); |
| MElementContainer<MUIElement> parent = changedObj.getParent(); |
| if (parent == null) { |
| return; |
| } |
| if (changedObj.isVisible()) { |
| if (parent.getRenderer() != null) { |
| Object myParent = ((AbstractPartRenderer) parent.getRenderer()) |
| .getUIContainer(changedObj); |
| if (myParent instanceof Composite) { |
| Composite parentComp = (Composite) myParent; |
| ctrl.setParent(parentComp); |
| |
| Control prevControl = null; |
| for (MUIElement childME : parent.getChildren()) { |
| if (childME == changedObj) |
| break; |
| if (childME.getWidget() instanceof Control && childME.isVisible()) { |
| prevControl = (Control) childME.getWidget(); |
| } |
| } |
| if (prevControl != null) |
| ctrl.moveBelow(prevControl); |
| else |
| ctrl.moveAbove(null); |
| ctrl.getShell().layout(new Control[] { ctrl }, SWT.DEFER); |
| } |
| |
| // Check if the parent is visible |
| if (!parent.isVisible()) |
| parent.setVisible(true); |
| } |
| } else { |
| Shell limbo = (Shell) app.getContext().get("limbo"); |
| |
| // Reparent the control to 'limbo' |
| Composite curParent = ctrl.getParent(); |
| ctrl.setParent(limbo); |
| curParent.layout(true); |
| if (curParent.getShell() != curParent) |
| curParent.getShell().layout(new Control[] { curParent }, SWT.DEFER); |
| |
| // Always leave Window's in the presentation |
| if ((Object) parent instanceof MWindow) |
| return; |
| |
| // If there are no more 'visible' children then make the parent go away too |
| boolean makeParentInvisible = true; |
| for (MUIElement kid : parent.getChildren()) { |
| if (kid.isToBeRendered() && kid.isVisible()) { |
| makeParentInvisible = false; |
| break; |
| } |
| } |
| |
| // Special check: If a perspective goes invisibe we need to make its |
| // PerspectiveStack invisible as well...see bug 369528 |
| if (makeParentInvisible || changedObj instanceof MPerspective) |
| parent.setVisible(false); |
| } |
| } |
| } |
| }; |
| |
| private EventHandler renderingChangeHandler = new EventHandler() { |
| public void handleEvent(Event event) { |
| MUIElement changedObj = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT); |
| MElementContainer<MUIElement> container = null; |
| if (changedObj.getCurSharedRef() != null) |
| container = changedObj.getCurSharedRef().getParent(); |
| else |
| container = changedObj.getParent(); |
| |
| // this can happen for shared parts that aren't attached to any placeholders |
| if (container == null) { |
| return; |
| } |
| |
| // never hide top-level windows |
| MUIElement containerElement = container; |
| if (containerElement instanceof MWindow && containerElement.getParent() != null) { |
| return; |
| } |
| |
| // These elements should neither be shown nor hidden based on their containment state |
| if (isLastEditorStack(containerElement) || containerElement instanceof MPerspective |
| || containerElement instanceof MPerspectiveStack) |
| return; |
| |
| Boolean toBeRendered = (Boolean) event.getProperty(UIEvents.EventTags.NEW_VALUE); |
| if (toBeRendered) { |
| // Bring the container back if one of its children goes visible |
| if (!container.isToBeRendered()) |
| container.setToBeRendered(true); |
| } else { |
| int visCount = modelService.countRenderableChildren(container); |
| |
| // Remove stacks with no visible children from the display (but not the |
| // model) |
| final MElementContainer<MUIElement> theContainer = container; |
| if (visCount == 0) { |
| Display.getCurrent().asyncExec(new Runnable() { |
| public void run() { |
| if (!isLastEditorStack(theContainer)) |
| theContainer.setToBeRendered(false); |
| } |
| }); |
| } |
| } |
| } |
| }; |
| |
| @PostConstruct |
| void init(IEclipseContext context) { |
| eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_CHILDREN, childrenHandler); |
| eventBroker.subscribe(UIEvents.UIElement.TOPIC_TOBERENDERED, renderingChangeHandler); |
| eventBroker.subscribe(UIEvents.UIElement.TOPIC_VISIBLE, visibilityChangeHandler); |
| } |
| |
| @PreDestroy |
| void removeListeners() { |
| eventBroker.unsubscribe(childrenHandler); |
| eventBroker.unsubscribe(renderingChangeHandler); |
| eventBroker.unsubscribe(visibilityChangeHandler); |
| } |
| |
| boolean isLastEditorStack(MUIElement element) { |
| return modelService.isLastEditorStack(element); |
| } |
| } |