blob: 9a4b911436be92b307d74db058fda722304684b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2009 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.renderers.swt;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.services.events.IEventBroker;
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.MPerspective;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.workbench.swt.internal.AbstractPartRenderer;
import org.eclipse.e4.workbench.ui.UIEvents;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Widget;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
/**
* This class encapsulates the functionality necessary to manage stacks of parts
* in a 'lazy loading' manner. For these stacks only the currently 'active'
* child <b>most</b> be rendered so in this class we over ride that default
* behavior for processing the stack's contents to prevent all of the contents
* from being rendered, calling 'childAdded' instead. This not only saves time
* and SWT resources but is necessary in an IDE world where we must not
* arbitrarily cause plug-in loading.
*
*/
public abstract class LazyStackRenderer extends SWTPartRenderer {
private EventHandler lazyLoader = new EventHandler() {
public void handleEvent(Event event) {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(element instanceof MGenericStack<?>))
return;
MGenericStack<MUIElement> stack = (MGenericStack<MUIElement>) element;
LazyStackRenderer lsr = (LazyStackRenderer) stack.getRenderer();
if (lsr == null)
return;
// Gather up the elements that are being 'hidden' by this change
MUIElement oldSel = (MUIElement) event
.getProperty(UIEvents.EventTags.OLD_VALUE);
if (oldSel != null) {
List<MUIElement> goingHidden = new ArrayList<MUIElement>();
hideElementRecursive(oldSel, goingHidden);
}
if (stack.getSelectedElement() != null)
lsr.showTab(stack.getSelectedElement());
}
};;
public LazyStackRenderer() {
super();
}
public void init(IEventBroker eventBroker) {
// Ensure that there only ever *one* listener. Each subclass
// will call this method
eventBroker.unsubscribe(lazyLoader);
eventBroker.subscribe(UIEvents.buildTopic(
UIEvents.ElementContainer.TOPIC,
UIEvents.ElementContainer.SELECTEDELEMENT), lazyLoader);
}
/**
* @param eventBroker
*/
public void contextDisposed(IEventBroker eventBroker) {
eventBroker.unsubscribe(lazyLoader);
}
public void postProcess(MUIElement element) {
if (!(element instanceof MGenericStack<?>))
return;
MGenericStack<MUIElement> stack = (MGenericStack<MUIElement>) element;
MUIElement selPart = stack.getSelectedElement();
if (selPart != null) {
showTab(selPart);
} else if (stack.getChildren().size() > 0) {
// NOTE: This code will cause a SELECTED_ELEMENT change on the
// stack, leading to the tab being shown
selPart = stack.getChildren().get(0);
stack.setSelectedElement(selPart);
}
}
@Override
public void processContents(MElementContainer<MUIElement> me) {
Widget parentWidget = getParentWidget(me);
if (parentWidget == null)
return;
// Lazy Loading: here we only process the contents through childAdded,
// we specifically do not render them
for (MUIElement part : me.getChildren()) {
if (part.isToBeRendered())
createTab(me, part);
}
}
/**
* This method is necessary to allow the parent container to show affordance
* (i.e. tabs) for child elements -without- creating the actual part
*
* @param me
* The parent model element
* @param part
* The child to show the affordance for
*/
protected void createTab(MElementContainer<MUIElement> me, MUIElement part) {
}
protected void showTab(MUIElement element) {
// Now process any newly visible elements
List<MUIElement> becomingVisible = new ArrayList<MUIElement>();
MUIElement curSel = element.getParent().getSelectedElement();
if (curSel != null) {
showElementRecursive(curSel, becomingVisible, null);
}
}
private void hideElementRecursive(MUIElement element,
List<MUIElement> goingHidden) {
if (element == null || element.getWidget() == null)
return;
if (element instanceof MPlaceholder) {
element = ((MPlaceholder) element).getRef();
}
// Hide any floating windows
if (element instanceof MWindow && element.getWidget() != null) {
element.setVisible(false);
}
goingHidden.add(element);
if (element instanceof MGenericStack<?>) {
// For stacks only the currently selected elements are being hidden
MGenericStack<?> container = (MGenericStack<?>) element;
MUIElement curSel = container.getSelectedElement();
hideElementRecursive(curSel, goingHidden);
} else if (element instanceof MElementContainer<?>) {
MElementContainer<?> container = (MElementContainer<?>) element;
for (MUIElement childElement : container.getChildren()) {
hideElementRecursive(childElement, goingHidden);
}
// OK, now process detached windows
if (element instanceof MWindow) {
for (MWindow w : ((MWindow) element).getWindows()) {
hideElementRecursive(w, goingHidden);
}
} else if (element instanceof MPerspective) {
for (MWindow w : ((MPerspective) element).getWindows()) {
hideElementRecursive(w, goingHidden);
}
}
}
}
private void showElementRecursive(MUIElement element,
List<MUIElement> becomingVisible, IEclipseContext phParentContext) {
if (!element.isToBeRendered())
return;
if (element instanceof MPlaceholder && element.getWidget() != null) {
MPlaceholder ph = (MPlaceholder) element;
MUIElement ref = ph.getRef();
phParentContext = getContext(ph);
Composite phComp = (Composite) ph.getWidget();
Control refCtrl = (Control) ph.getRef().getWidget();
refCtrl.setParent(phComp);
phComp.layout(new Control[] { refCtrl }, SWT.DEFER);
element = ref;
}
if (element instanceof MContext && phParentContext != null) {
IEclipseContext context = ((MContext) element).getContext();
if (context.getParent() != phParentContext)
context.setParent(phParentContext);
phParentContext = null;
}
// Show any floating windows
if (element instanceof MWindow && element.getWidget() != null) {
element.setVisible(true);
}
becomingVisible.add(element);
if (element instanceof MGenericStack<?>) {
// For stacks only the currently selected elements are being visible
MGenericStack<?> container = (MGenericStack<?>) element;
MUIElement curSel = container.getSelectedElement();
if (curSel == null && container.getChildren().size() > 0)
curSel = container.getChildren().get(0);
if (curSel != null)
showElementRecursive(curSel, becomingVisible, phParentContext);
} else if (element instanceof MElementContainer<?>) {
MElementContainer<?> container = (MElementContainer<?>) element;
List<MUIElement> kids = new ArrayList<MUIElement>(
container.getChildren());
for (MUIElement childElement : kids) {
showElementRecursive(childElement, becomingVisible,
phParentContext);
}
// OK, now process detached windows
if (element instanceof MWindow) {
for (MWindow w : ((MWindow) element).getWindows()) {
showElementRecursive(w, becomingVisible, phParentContext);
}
} else if (element instanceof MPerspective) {
for (MWindow w : ((MPerspective) element).getWindows()) {
showElementRecursive(w, becomingVisible, phParentContext);
}
}
}
}
public void swap(MPlaceholder placeholder) {
MUIElement element = placeholder.getRef();
MElementContainer<MUIElement> elementParent = element.getParent();
int elementIndex = elementParent.getChildren().indexOf(element);
MElementContainer<MUIElement> phParent = placeholder.getParent();
int phIndex = phParent.getChildren().indexOf(placeholder);
// Remove the two elements from their respective parents
elementParent.getChildren().remove(element);
phParent.getChildren().remove(placeholder);
// swap over the UIElement info
boolean onTop = element.isOnTop();
boolean vis = element.isVisible();
boolean tbr = element.isToBeRendered();
String cd = element.getContainerData();
element.setOnTop(placeholder.isOnTop());
element.setVisible(placeholder.isVisible());
element.setToBeRendered(placeholder.isToBeRendered());
element.setContainerData(placeholder.getContainerData());
placeholder.setOnTop(onTop);
placeholder.setVisible(vis);
placeholder.setToBeRendered(tbr);
placeholder.setContainerData(cd);
// Add the elements back into the new parents
elementParent.getChildren().add(elementIndex, placeholder);
phParent.getChildren().add(phIndex, element);
if (elementParent.getSelectedElement() == element)
elementParent.setSelectedElement(null);
if (phParent.getSelectedElement() == null)
phParent.setSelectedElement(element);
// directly manage the widget reparent if the parent exists
if (element.getWidget() instanceof Control) {
// Swap the control's layout data
// HACK!! for now use the placeholder's 'renderer' att to hold the
// value
Control c = (Control) element.getWidget();
Object phLayoutData = placeholder.getRenderer();
placeholder.setRenderer(c.getLayoutData());
c.setLayoutData(phLayoutData);
// If the new parent has already been rendered directly move the
// control
if (phParent.getWidget() instanceof Composite) {
AbstractPartRenderer renderer = (AbstractPartRenderer) phParent
.getRenderer();
Composite newParent = (Composite) renderer
.getUIContainer(element);
c.setParent(newParent);
Control[] changed = { c };
// Fix the Z-order
MUIElement prevElement = null;
for (MUIElement kid : phParent.getChildren()) {
if (kid == element) {
if (prevElement == null) {
c.moveAbove(null); // first one, on top
} else {
c.moveBelow((Control) prevElement.getWidget());
}
} else if (kid.getWidget() != null) {
prevElement = kid;
}
}
newParent.getShell().layout(changed, SWT.CHANGED | SWT.DEFER);
if (newParent instanceof CTabFolder) {
CTabFolder ctf = (CTabFolder) newParent;
if (ctf.getSelection() == null) {
ctf.setSelection(0);
}
}
}
}
}
}