blob: 61491d61511dc30cb0361dd2a0bcec0525ac2f52 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Rushan R. Gilmullin and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Rushan R. Gilmullin - initial API and implementation
*******************************************************************************/
package org.eclipse.osbp.vaaclipse.presentation.renderers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
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.MUILabel;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
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.MStackElement;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.services.internal.events.EventBroker;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
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.emf.ecore.EObject;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.osbp.runtime.designer.api.IDesignerService;
import org.eclipse.osbp.runtime.designer.api.IWidgetDesignConfigurator;
import org.eclipse.osbp.vaaclipse.api.VaadinExecutorService;
import org.eclipse.osbp.vaaclipse.presentation.dnd.VaadinDropHandler;
import org.eclipse.osbp.vaaclipse.publicapi.change.SimpleCommand;
import org.eclipse.osbp.vaaclipse.publicapi.resources.ResourceHelper;
import org.eclipse.osbp.vaaclipse.widgets.StackWidget;
import org.eclipse.osbp.vaaclipse.widgets.StackWidget.StateListener;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import com.vaadin.server.Resource;
import com.vaadin.ui.Component;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TabSheet.CloseHandler;
import com.vaadin.ui.TabSheet.SelectedTabChangeEvent;
import com.vaadin.ui.TabSheet.SelectedTabChangeListener;
import com.vaadin.ui.TabSheet.Tab;
import fi.jasoft.dragdroplayouts.client.ui.LayoutDragMode;
@SuppressWarnings("restriction")
public class StackRenderer extends VaadinRenderer implements IDesignerService.IDesignListener {
@Inject
private EventBroker eventBroker;
@Inject
private EModelService modelService;
@Inject
private EditingDomain editingDomain;
@Inject
private EPartService partService;
private Map<Component, MStackElement> vaatab2Element = new HashMap<Component, MStackElement>();
private boolean ignoreTabSelChanges = false;
@Inject
VaadinExecutorService communicationManager;
@Inject
@Optional
private IDesignerService designerService;
private EventHandler tagListener = new EventHandler() {
@Override
public void handleEvent(Event event) {
Object changedObj = event.getProperty(EventTags.ELEMENT);
String eventType = (String) event.getProperty(UIEvents.EventTags.TYPE);
String tag = (String) event.getProperty(UIEvents.EventTags.NEW_VALUE);
int location = modelService.getElementLocation((MUIElement) changedObj);
if (!(changedObj instanceof MPartStack) || location == EModelService.IN_SHARED_AREA) {
return;
}
final MPartStack stack = (MPartStack) changedObj;
StackWidget stackWidget = (StackWidget) stack.getWidget();
if (UIEvents.EventTypes.ADD.equals(eventType)) {
if (IPresentationEngine.MINIMIZED.equals(tag)) {
stackWidget.setState(-1);
} else if (IPresentationEngine.MAXIMIZED.equals(tag)) {
stackWidget.setState(1);
}
} else if (UIEvents.EventTypes.REMOVE.equals(eventType)) {
stackWidget.setState(0);
}
}
};
private EventHandler selectElementHandler = new EventHandler() {
public void handleEvent(Event event) {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(element instanceof MPartStack))
return;
MPartStack stack = (MPartStack) element;
if (stack.getRenderer() != StackRenderer.this)
return;
if (stack.getSelectedElement() != null) {
if (stack.getSelectedElement().getWidget() == null) {
IPresentationEngine engine = (IPresentationEngine) context.get(IPresentationEngine.class.getName());
engine.createGui(stack.getSelectedElement());
int i = 0;
for (MStackElement e : stack.getChildren()) {
if (e == stack.getSelectedElement())
break;
if (e.getWidget() != null)
i++;
}
addTab((TabSheet) stack.getWidget(), stack.getSelectedElement(), i);
}
if (stack.getSelectedElement() instanceof MPart) {
MPart selected = (MPart) stack.getSelectedElement();
if (selected.getObject() == null) {
PartRenderer.renderPartContent(selected, selected.getContext());
}
}
ignoreTabSelChanges = true;
((TabSheet) stack.getWidget()).setSelectedTab((Component) stack.getSelectedElement().getWidget());
ignoreTabSelChanges = false;
}
}
};
EventHandler itemUpdater = new EventHandler() {
public void handleEvent(Event event) {
MUIElement element = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(element instanceof MPart))
return;
MPart part = (MPart) element;
String attName = (String) event.getProperty(UIEvents.EventTags.ATTNAME);
Object newValue = event.getProperty(UIEvents.EventTags.NEW_VALUE);
MPartStack stack = null;
// is this a direct child of the stack?
MPlaceholder placeholder = null;
if (element.getParent() != null && element.getParent().getRenderer() == StackRenderer.this) {
stack = (MPartStack) (MElementContainer<?>) element.getParent();
} else {
// Do we have any stacks with place holders for the element
// that's changed?
MWindow win = modelService.getTopLevelWindowFor(part);
List<MPlaceholder> refs = modelService.findElements(win, null, MPlaceholder.class, null);
if (refs != null) {
for (MPlaceholder ref : refs) {
if (ref.getRef() != part)
continue;
MElementContainer<?> refParent = ref.getParent();
// can be null, see bug 328296
if (refParent != null && refParent.getRenderer() instanceof StackRenderer) {
placeholder = ref;
stack = (MPartStack) refParent;
}
}
}
}
if (stack != null) {
Tab tab = ((StackWidget) stack.getWidget())
.getTab((Component) (placeholder == null ? part.getWidget() : placeholder.getWidget()));
if (tab != null)
updateTab(tab, part, attName, newValue);
}
}
};
private void updateTab(Tab tab, MPart part, String attName, Object newValue) {
if (UIEvents.UILabel.LABEL.equals(attName)) {
String newName = (String) newValue;
tab.setCaption(getLabel(part, newName));
} else if (UIEvents.UILabel.ICONURI.equals(attName)) {
Resource icon = part.getIconURI() != null ? ResourceHelper.createResource(part.getIconURI()) : null;
tab.setIcon(icon);
} else if (UIEvents.UILabel.TOOLTIP.equals(attName)) {
String newTTip = (String) newValue;
tab.setDescription(newTTip);
} else if (UIEvents.UILabel.LOCALIZED_LABEL.equals(attName)) {
String newName = (String) newValue;
tab.setCaption(getLabel(part, newName));
} else if (UIEvents.UILabel.LOCALIZED_TOOLTIP.equals(attName)) {
String newTTip = (String) newValue;
tab.setDescription(newTTip);
} else if (UIEvents.Dirtyable.DIRTY.equals(attName)) {
Boolean dirtyState = (Boolean) newValue;
String text = tab.getCaption();
boolean hasAsterisk = text.length() > 0 && text.charAt(0) == '*';
if (dirtyState.booleanValue()) {
if (!hasAsterisk) {
tab.setCaption('*' + text);
}
} else if (hasAsterisk) {
tab.setCaption(text.substring(1));
}
}
}
private String getLabel(MUILabel itemPart, String newName) {
if (newName == null) {
newName = ""; //$NON-NLS-1$
}
if (itemPart instanceof MDirtyable && ((MDirtyable) itemPart).isDirty()) {
newName = '*' + newName;
}
return newName;
}
@PostConstruct
public void postConstruct() {
eventBroker.subscribe(UIEvents.ApplicationElement.TOPIC_TAGS, tagListener);
eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT, selectElementHandler);
eventBroker.subscribe(UIEvents.UILabel.TOPIC_ALL, itemUpdater);
eventBroker.subscribe(UIEvents.Dirtyable.TOPIC_DIRTY, itemUpdater);
eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_CHILDREN, childrenMoveUpdater);
}
@Override
public void notify(IDesignerService.DesignEvent event) {
updateDesigner(event.getType() == IDesignerService.EventType.ENABLED);
}
private void updateDesigner(boolean enabled) {
IWidgetDesignConfigurator designConfigurator = context.get(IWidgetDesignConfigurator.class);
if (designConfigurator != null) {
for (Entry<Component, MStackElement> entry : vaatab2Element.entrySet()) {
designConfigurator.configure(entry.getKey(), (EObject) entry.getValue(), enabled);
}
}
}
@PreDestroy
public void preDestroy() {
if (designerService != null) {
designerService.removeListener(this);
}
eventBroker.unsubscribe(tagListener);
eventBroker.unsubscribe(selectElementHandler);
eventBroker.unsubscribe(itemUpdater);
eventBroker.unsubscribe(childrenMoveUpdater);
}
// TODO for later use
private EventHandler childrenMoveUpdater = new EventHandler() {
public void handleEvent(Event event) {
// Ensure that this event is for a MMenuItem
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MPartStack))
return;
@SuppressWarnings("unchecked")
MElementContainer<MUIElement> stack = (MElementContainer<MUIElement>) event
.getProperty(UIEvents.EventTags.ELEMENT);
String type = (String) event.getProperty(UIEvents.EventTags.TYPE);
if (UIEvents.EventTypes.MOVE.equals(type)) {
StackWidget stackWidget = (StackWidget) stack.getWidget();
stackWidget.removeAllComponents();
processContents(stack);
}
}
};
@Override
public void createWidget(MUIElement element, MElementContainer<MUIElement> parent) {
if (!(element instanceof MPartStack))
return;
MPartStack stack = (MPartStack) element;
StackWidget stackWidget = new StackWidget();
stackWidget.setDragMode(LayoutDragMode.CLONE);
stackWidget.setSizeFull();
element.setWidget(stackWidget);
IEclipseContext context = modelService.getContainingContext(stack).createChild();
context.set(MPartStack.class, stack);
VaadinDropHandler dropHandler = ContextInjectionFactory.make(VaadinDropHandler.class, context);
stackWidget.setDropHandler(dropHandler);
if (designerService != null) {
designerService.addListener(this);
if (designerService.isDesignMode()) {
updateDesigner(true);
}
}
}
@Override
public void processContents(final MElementContainer<MUIElement> container) {
MPartStack stack = (MPartStack) (MElementContainer<?>) container;
StackWidget stackWidget = (StackWidget) stack.getWidget();
for (MStackElement element : stack.getChildren()) {
if (element.isToBeRendered())
addTab(stackWidget, (MStackElement) element, stackWidget.getComponentCount());
}
// if there are childs in stack and the selected element is not
// specified for stack, set the first child as selected
if (stack.getChildren().size() > 0 && stack.getSelectedElement() == null) {
if (stack.getChildren().get(0).isVisible() && stack.getChildren().get(0).isToBeRendered()) {
stack.setSelectedElement(stack.getChildren().get(0));
}
}
if (stack.getSelectedElement() != null) {
Component stackSelectedComponent = (Component) stack.getSelectedElement().getWidget();
stackWidget.setSelectedTab(stackSelectedComponent);
}
}
private void addTab(TabSheet parentPane, MStackElement element, int pos) {
MUILabel mLabel;
if (element instanceof MPlaceholder)
mLabel = (MUILabel) ((MPlaceholder) element).getRef();
else
mLabel = (MUILabel) element;
boolean closable = false;
if (mLabel instanceof MPart)
closable = ((MPart) mLabel).isCloseable();
// Tab tab = parentPane.addTab((com.vaadin.ui.Component)
// element.getWidget(), mLabel.getLocalizedLabel(), icon, pos);
Tab tab = parentPane.addTab((com.vaadin.ui.Component) element.getWidget(), pos);
tab.setCaption(mLabel.getLocalizedLabel());
Resource icon = (mLabel.getIconURI() != null && !mLabel.getIconURI().trim().equals(""))
? ResourceHelper.createResource(mLabel.getIconURI()) : null;
if (icon != null) {
tab.setIcon(icon);
}
tab.setClosable(closable);
tab.setDescription(mLabel.getLocalizedTooltip());
vaatab2Element.put((Component) element.getWidget(), element);
if (designerService != null && designerService.isDesignMode()) {
updateDesigner(designerService.isDesignMode());
}
}
@SuppressWarnings("serial")
@Override
public void hookControllerLogic(final MUIElement element) {
final StackWidget sw = (StackWidget) element.getWidget();
int location = modelService.getElementLocation(element);
if (location != EModelService.IN_SHARED_AREA) // if the stack not in
// shared area
{
sw.addStateListener(new StateListener() {
@Override
public void stateChanged(int newState, int oldState) {
if (oldState == 0 && newState == 1)
setState(element, IPresentationEngine.MAXIMIZED);
else if (oldState == 1 && newState == 0)
setState(element, null);
else if (oldState == 1 && newState == -1) {
element.getTags().remove(IPresentationEngine.MINIMIZED);
element.getTags().remove(IPresentationEngine.MAXIMIZED);
element.getTags().add(IPresentationEngine.MINIMIZED);
} else if (oldState == -1 && newState == 0) {
element.getTags().remove(IPresentationEngine.MINIMIZED_BY_ZOOM);
element.getTags().remove(IPresentationEngine.MINIMIZED);
} else if (oldState == 0 && newState == -1)
setState(element, IPresentationEngine.MINIMIZED);
}
private void setState(MUIElement element, String state) {
element.getTags().remove(IPresentationEngine.MINIMIZED_BY_ZOOM);
if (IPresentationEngine.MINIMIZED.equals(state)) {
element.getTags().remove(IPresentationEngine.MAXIMIZED);
element.getTags().add(IPresentationEngine.MINIMIZED);
} else if (IPresentationEngine.MAXIMIZED.equals(state)) {
element.getTags().remove(IPresentationEngine.MINIMIZED);
element.getTags().add(IPresentationEngine.MAXIMIZED);
} else {
element.getTags().remove(IPresentationEngine.MINIMIZED);
element.getTags().remove(IPresentationEngine.MAXIMIZED);
}
}
});
}
sw.addSelectedTabChangeListener(new SelectedTabChangeListener() {
public void selectedTabChange(SelectedTabChangeEvent event) {
final MStackElement stackElement = vaatab2Element.get(sw.getSelectedTab());
if (stackElement != null) {
if (ignoreTabSelChanges)
return;
final MPartStack stack = (MPartStack) (MElementContainer<?>) stackElement.getParent();
if (stack != null && stack.getSelectedElement() != stackElement) {
SimpleCommand command = new SimpleCommand("Switch stack element") {
final MStackElement oldStackElement = stack.getSelectedElement();
@Override
protected void doUndo() {
stack.setSelectedElement(oldStackElement);
activateStack(stack);
}
@Override
protected void doExecute() {
stack.setSelectedElement(stackElement);
activateStack(stack);
}
};
editingDomain.getCommandStack().execute(command);
}
}
}
});
sw.setCloseHandler(new CloseHandler() {
public void onTabClose(TabSheet tabsheet, Component tabContent) {
MStackElement stackElement = vaatab2Element.get(tabContent);
closePart(stackElement);
}
});
}
private void activateStack(MPartStack stack) {
communicationManager.invokeLater(new ActivationRunnable(stack));
}
private class ActivationRunnable implements Runnable {
private MPartStack stack;
public ActivationRunnable(MPartStack stack) {
this.stack = stack;
}
@Override
public void run() {
MStackElement stackElement = stack.getSelectedElement();
// Ensure we're activating a stack in the current perspective,
// when using a dialog to open a perspective
// we end up in the situation where this stack is in the
// previously active perspective
int location = modelService.getElementLocation(stack);
if ((location & EModelService.IN_ACTIVE_PERSPECTIVE) == 0
&& (location & EModelService.OUTSIDE_PERSPECTIVE) == 0
&& (location & EModelService.IN_SHARED_AREA) == 0)
return;
if (!isValid(stackElement))
return;
if (stackElement instanceof MPlaceholder)
stackElement = (MStackElement) ((MPlaceholder) stackElement).getRef();
IEclipseContext curContext = getContext(stackElement);
if (curContext != null) {
EPartService ps = (EPartService) curContext.get(EPartService.class.getName());
if (ps != null)
ps.activate((MPart) stackElement, true);
}
}
}
private boolean isValid(MUIElement element) {
if (element == null || !element.isToBeRendered()) {
return false;
}
if (element instanceof MApplication) {
return true;
}
MUIElement parent = element.getParent();
if (parent == null && element instanceof MWindow) {
// might be a detached window
parent = (MUIElement) ((EObject) element).eContainer();
}
if (parent == null) {
// might be a shared part, try to find the placeholder
MWindow window = modelService.getTopLevelWindowFor(element);
return window == null ? false : isValid(modelService.findPlaceholderFor(window, element));
}
return isValid(parent);
}
private boolean isClosable(MPart part) {
// if it's a shared part check its current ref
if (part.getCurSharedRef() != null) {
return !(part.getCurSharedRef().getTags().contains(IPresentationEngine.NO_CLOSE));
}
return part.isCloseable();
}
private boolean closePart(MStackElement stackElement) {
MPart part = (MPart) ((stackElement instanceof MPart) ? stackElement : ((MPlaceholder) stackElement).getRef());
if (!isClosable(part)) {
return false;
}
IEclipseContext partContext = part.getContext();
IEclipseContext parentContext = getContextForParent(part);
// a part may not have a context if it hasn't been rendered
IEclipseContext context = partContext == null ? parentContext : partContext;
// Allow closes to be 'canceled'
EPartService partService = (EPartService) context.get(EPartService.class.getName());
if (partService.savePart(part, true)) {
partService.hidePart(part);
return true;
}
// the user has canceled out of the save operation, so don't close the
// part
return false;
}
@Override
public void setVisible(MUIElement changedElement, boolean visible) {
TabSheet tabPane = (TabSheet) changedElement.getWidget();
tabPane.setVisible(visible);
}
@Override
public void addChildGui(MUIElement child, MElementContainer<MUIElement> element) {
if (!(child instanceof MStackElement))
return;
StackWidget sw = (StackWidget) element.getWidget();
int index = indexOf(child, element);
addTab(sw, (MStackElement) child, index);
}
}