blob: 63b260b4c4013f5a819aa16f65d252e20299dca4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2013 EclipseSource Muenchen GmbH 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:
* Anas Chakfeh - initial API and implementation
* Eugen Neufeld - Refactoring
* Alexandra Buzila - Refactoring
* Johannes Faltermeier - integration with validation service
******************************************************************************/
package org.eclipse.emf.ecp.view.spi.treemasterdetail.ui.swt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.common.spi.ChildrenDescriptorCollector;
import org.eclipse.emf.ecp.edit.internal.swt.util.OverlayImageDescriptor;
import org.eclipse.emf.ecp.edit.spi.DeleteService;
import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
import org.eclipse.emf.ecp.edit.spi.ReferenceService;
import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
import org.eclipse.emf.ecp.ui.view.ECPRendererException;
import org.eclipse.emf.ecp.ui.view.swt.ECPSWTViewRenderer;
import org.eclipse.emf.ecp.view.internal.swt.ContextMenuViewModelService;
import org.eclipse.emf.ecp.view.internal.treemasterdetail.ui.swt.Activator;
import org.eclipse.emf.ecp.view.model.common.edit.provider.CustomReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContextFactory;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
import org.eclipse.emf.ecp.view.spi.model.VElement;
import org.eclipse.emf.ecp.view.spi.model.VView;
import org.eclipse.emf.ecp.view.spi.model.VViewModelProperties;
import org.eclipse.emf.ecp.view.spi.model.reporting.StatusReport;
import org.eclipse.emf.ecp.view.spi.model.util.ViewModelPropertiesHelper;
import org.eclipse.emf.ecp.view.spi.provider.ViewProviderHelper;
import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
import org.eclipse.emf.ecp.view.treemasterdetail.model.VTreeMasterDetail;
import org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.internal.RootObject;
import org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.internal.TreeMasterDetailSelectionManipulatorHelper;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.command.DeleteCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.action.ecp.CreateChildAction;
import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emfforms.common.Optional;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer;
import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ToolBar;
import org.osgi.framework.FrameworkUtil;
/**
* SWT Renderer for a {@link VTreeMasterDetail} element.
*
* @author Anas Chakfeh
* @author Eugen Neufeld
* @since 1.5
*
*/
@SuppressWarnings("deprecation")
public class TreeMasterDetailSWTRenderer extends AbstractSWTRenderer<VTreeMasterDetail> {
private static final String ENABLE_MULTI_EDIT = "enableMultiEdit"; //$NON-NLS-1$
/**
* Default Constructor.
*
* @param vElement the view element to be rendered
* @param viewContext The view model context
* @param reportService the ReportService to use
* @since 1.6
*/
@Inject
public TreeMasterDetailSWTRenderer(final VTreeMasterDetail vElement, final ViewModelContext viewContext,
ReportService reportService) {
super(vElement, viewContext, reportService);
}
/**
* The detail key passed to the view model context.
*/
public static final String DETAIL_KEY = "detail"; //$NON-NLS-1$
/**
* Context key for the root.
*/
public static final String ROOT_KEY = "root"; //$NON-NLS-1$
private SWTGridDescription rendererGridDescription;
private Font detailsFont;
private Color titleColor;
private Font titleFont;
private Color headerBgColor;
private TreeViewer treeViewer;
/**
* Static string.
*
*/
public static final String GLOBAL_ADDITIONS = "global_additions"; //$NON-NLS-1$
private ScrolledComposite rightPanel;
private Composite container;
private Composite rightPanelContainerComposite;
private ModelChangeListener domainModelListener;
/**
* @author jfaltermeier
*
*/
private final class MasterTreeContextMenuListener implements IMenuListener {
private final EditingDomain editingDomain;
private final TreeViewer treeViewer;
private final ChildrenDescriptorCollector childrenDescriptorCollector;
private final List<MasterDetailAction> menuActions;
/**
* @param editingDomain
* @param treeViewer
* @param childrenDescriptorCollector
* @param menuActions
*/
private MasterTreeContextMenuListener(EditingDomain editingDomain, TreeViewer treeViewer,
ChildrenDescriptorCollector childrenDescriptorCollector, List<MasterDetailAction> menuActions) {
this.editingDomain = editingDomain;
this.treeViewer = treeViewer;
this.childrenDescriptorCollector = childrenDescriptorCollector;
this.menuActions = menuActions;
}
@Override
public void menuAboutToShow(IMenuManager manager) {
if (getVElement().isEffectivelyReadonly() || !getVElement().isEffectivelyEnabled()) {
return;
}
if (treeViewer.getSelection().isEmpty()) {
fillMenu(null, manager);
return;
}
final EObject root = ((RootObject) treeViewer.getInput()).getRoot();
if (treeViewer.getSelection() instanceof IStructuredSelection) {
final IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
if (selection.size() == 1 && EObject.class.isInstance(selection.getFirstElement())) {
final EObject eObject = (EObject) selection.getFirstElement();
final EditingDomain domain = AdapterFactoryEditingDomain.getEditingDomainFor(eObject);
if (domain == null) {
return;
}
final Collection<?> descriptors = childrenDescriptorCollector.getDescriptors(eObject);
fillContextMenu(manager, descriptors, editingDomain, eObject);
}
if (!selection.toList().contains(root)) {
manager.add(new Separator(GLOBAL_ADDITIONS));
addDeleteActionToContextMenu(editingDomain, manager, selection);
}
manager.add(new Separator());
if (selection.getFirstElement() != null && EObject.class.isInstance(selection.getFirstElement())) {
final EObject selectedObject = (EObject) selection.getFirstElement();
fillMenu(selectedObject, manager);
}
}
}
private void fillMenu(final EObject selectedObject, IMenuManager manager) {
for (final MasterDetailAction menuAction : menuActions) {
if (menuAction.shouldShow(selectedObject)) {
final Action newAction = new Action() {
@Override
public void run() {
super.run();
menuAction.execute(selectedObject);
}
};
newAction.setImageDescriptor(ImageDescriptor.createFromURL(FrameworkUtil.getBundle(
menuAction.getClass())
.getResource(menuAction.getImagePath())));
newAction.setText(menuAction.getLabel());
manager.add(newAction);
}
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#dispose()
*/
@Override
protected void dispose() {
rendererGridDescription = null;
if (getViewModelContext() != null && domainModelListener != null) {
getViewModelContext().unregisterDomainChangeListener(domainModelListener);
}
domainModelListener = null;
if (treeViewer != null) {
treeViewer.setInput(null);
}
super.dispose();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#getGridDescription(SWTGridDescription)
*/
@Override
public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
if (rendererGridDescription == null) {
rendererGridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
}
return rendererGridDescription;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#renderControl(org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell,
* org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control renderControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
NoPropertyDescriptorFoundExeption {
/* The tree's composites */
final Composite form = createMasterDetailForm(parent);
createHeader(form);
final SashForm sash = createSash(form);
final Composite masterPanel = createMasterPanel(sash);
createRightPanelContent(sash);
sash.setWeights(new int[] { 1, 3 });
createMasterTree(masterPanel);
if (hasContextMenu()) {
registerControlAsContextMenuReceiver();
}
form.layout(true);
return form;
}
private void registerControlAsContextMenuReceiver() {
if (!getViewModelContext().hasService(ContextMenuViewModelService.class)) {
return;
}
final ContextMenuViewModelService service = getViewModelContext().getService(
ContextMenuViewModelService.class);
if (service != null) {
service.setParentControl(treeViewer.getTree());
service.registerContextMenu();
}
}
/**
* Creates the sashform for the master detail colums.
*
* @param parent the parent
* @return the sash
*/
protected SashForm createSash(Composite parent) {
/* THe contents of the composite */
final Composite sashComposite = new Composite(parent, SWT.FILL);
final GridLayout sashLayout = GridLayoutFactory.fillDefaults().create();
sashLayout.marginWidth = 5;
sashComposite.setLayout(sashLayout);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(sashComposite);
final SashForm sash = new SashForm(sashComposite, SWT.HORIZONTAL);
sash.setBackground(parent.getBackground());
GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).applyTo(sash);
GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(sash);
sash.setSashWidth(5);
return sash;
}
/**
* Create the parent of the master detail form.
*
* @param parent the parent
* @return the composite
*/
protected Composite createMasterDetailForm(Composite parent) {
final Composite form = new Composite(parent, SWT.BORDER);
final GridLayout layout = GridLayoutFactory.fillDefaults().create();
form.setLayout(layout);
form.setBackgroundMode(SWT.INHERIT_FORCE);
// form.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
return form;
}
/**
* Creates the tree viewer for the master.
*
* @param masterPanel the parent
* @return the tree viewer
*/
protected TreeViewer createMasterTree(final Composite masterPanel) {
final EObject modelElement = getViewModelContext().getDomainModel();
final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(modelElement);
final ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
new CustomReflectiveItemProviderAdapterFactory(),
new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
final AdapterFactoryContentProvider adapterFactoryContentProvider = new AdapterFactoryContentProvider(
adapterFactory) {
@Override
public Object[] getElements(Object object) {
return new Object[] { ((RootObject) object).getRoot() };
}
};
final AdapterFactoryLabelProvider labelProvider = new TreeMasterDetailLabelProvider(adapterFactory);
treeViewer = new TreeViewer(masterPanel);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).hint(100, SWT.DEFAULT)
.applyTo(treeViewer.getTree());
treeViewer.setContentProvider(adapterFactoryContentProvider);
treeViewer.setLabelProvider(getLabelProvider(labelProvider));
treeViewer.setAutoExpandLevel(2); // top level element is expanded, but not the children
treeViewer.setInput(new RootObject(modelElement));
// workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=27480
// the treeviewer doesn't autoexpand on refresh
domainModelListener = new ModelChangeListener() {
@Override
public void notifyChange(ModelChangeNotification notification) {
// expand the tree if elements are added to the tree and the root isn't already expanded
if (notification.getRawNotification().getEventType() == Notification.ADD
|| notification.getRawNotification().getEventType() == Notification.ADD_MANY) {
final EObject notifier = notification.getNotifier();
treeViewer.expandToLevel(notifier, 1);
}
}
};
getViewModelContext().registerDomainChangeListener(domainModelListener);
// Drag and Drop
if (hasDnDSupport()) {
addDragAndDropSupport(modelElement, treeViewer, editingDomain);
}
// Selection Listener
final TreeMasterViewSelectionListener treeMasterViewSelectionListener = new TreeMasterViewSelectionListener();
treeViewer.addSelectionChangedListener(treeMasterViewSelectionListener);
treeViewer.setSelection(new StructuredSelection(modelElement));
if (hasContextMenu()) {
fillContextMenu(treeViewer, editingDomain);
}
treeViewer.getTree().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent event) {
adapterFactoryContentProvider.dispose();
labelProvider.dispose();
adapterFactory.dispose();
if (titleFont != null) {
titleFont.dispose();
}
if (detailsFont != null) {
detailsFont.dispose();
}
if (titleColor != null) {
titleColor.dispose();
}
if (headerBgColor != null) {
headerBgColor.dispose();
}
treeViewer.removeSelectionChangedListener(treeMasterViewSelectionListener);
}
});
return treeViewer;
}
/**
* Return true if a context menu should be shown in the tree.
*
* @return true if a context menu should be shown, false otherwise
*/
protected boolean hasContextMenu() {
return true;
}
/**
* Return true if the tree should support DnD.
*
* @return true if DnD should be supported , false otherwise
*/
protected boolean hasDnDSupport() {
return true;
}
/**
* Returns the label provider.
*
* @param adapterFactoryLabelProvider the adaper factory label provider
* @return the label provider to use for the tree
*/
protected ILabelProvider getLabelProvider(final AdapterFactoryLabelProvider adapterFactoryLabelProvider) {
return adapterFactoryLabelProvider;
}
/**
* Creates the composite for the master panel.
*
* @param sash the parent
* @return the composite
*/
protected Composite createMasterPanel(final SashForm sash) {
final Composite leftPanel = new Composite(sash, SWT.NONE);
leftPanel.setLayout(GridLayoutFactory.fillDefaults().create());
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(leftPanel);
// leftPanel.setBackground(sash.getBackground());
leftPanel.setBackgroundMode(SWT.INHERIT_FORCE);
return leftPanel;
}
/**
* Adds the header to a parent composite.
*
* @param parent the parent
*/
protected void createHeader(Composite parent) {
final Composite headerComposite = new Composite(parent, SWT.NONE);
final GridLayout headerLayout = GridLayoutFactory.fillDefaults().create();
headerComposite.setLayout(headerLayout);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(headerComposite);
headerBgColor = new Color(parent.getDisplay(), new RGB(220, 240, 247));
headerComposite.setBackground(headerBgColor);
final EObject modelElement = getViewModelContext().getDomainModel();
final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(modelElement);
/* The header of the composite */
if (modelElement.eContainer() == null && !DynamicEObjectImpl.class.isInstance(modelElement)) {
final Composite header = getPageHeader(headerComposite);
final List<Action> actions = readToolbarActions(modelElement, editingDomain);
final ToolBar toolBar = new ToolBar(header, SWT.FLAT | SWT.RIGHT);
final FormData formData = new FormData();
formData.right = new FormAttachment(100, 0);
toolBar.setLayoutData(formData);
toolBar.layout();
final ToolBarManager toolBarManager = new ToolBarManager(toolBar);
/* Add actions to header */
for (final Action action : actions) {
toolBarManager.add(action);
}
toolBarManager.update(true);
header.layout();
}
}
private Composite getPageHeader(Composite parent) {
final Composite header = new Composite(parent, SWT.FILL);
final FormLayout layout = new FormLayout();
layout.marginHeight = 5;
layout.marginWidth = 5;
header.setLayout(layout);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(header);
header.setBackground(parent.getBackground());
final Label titleImage = new Label(header, SWT.FILL);
final ImageDescriptor imageDescriptor = ImageDescriptor.createFromURL(Activator.getDefault()
.getBundle()
.getResource("icons/view.png")); //$NON-NLS-1$
titleImage.setImage(new Image(parent.getDisplay(), imageDescriptor.getImageData()));
final FormData titleImageData = new FormData();
final int imageOffset = -titleImage.computeSize(SWT.DEFAULT, SWT.DEFAULT).y / 2;
titleImageData.top = new FormAttachment(50, imageOffset);
titleImageData.left = new FormAttachment(0, 10);
titleImage.setLayoutData(titleImageData);
final Label title = new Label(header, SWT.WRAP);
title.setText("View Editor"); //$NON-NLS-1$
titleFont = new Font(title.getDisplay(), getDefaultFontName(title), 12, SWT.BOLD);
title.setFont(titleFont);
title.setForeground(getTitleColor(parent));
final int titleHeight = title.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
final FormData titleData = new FormData();
titleData.left = new FormAttachment(titleImage, 5, SWT.DEFAULT);
titleData.top = new FormAttachment(50, -titleHeight / 2);
title.setLayoutData(titleData);
return header;
}
private Color getTitleColor(Composite parent) {
if (titleColor == null) {
titleColor = new Color(parent.getDisplay(), new RGB(25, 76, 127));
}
return titleColor;
}
/**
* Creates the composite holding the details.
*
* @param parent the parent
* @return the right panel/detail composite
*/
protected ScrolledComposite createRightPanelContent(Composite parent) {
rightPanel = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
rightPanel.setShowFocusedControl(true);
rightPanel.setExpandVertical(true);
rightPanel.setExpandHorizontal(true);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(rightPanel);
rightPanel.setLayout(GridLayoutFactory.fillDefaults().create());
rightPanel.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
container = new Composite(rightPanel, SWT.FILL);
container.setLayout(GridLayoutFactory.fillDefaults().create());
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(container);
container.setBackground(rightPanel.getBackground());
/* The header */
final Composite header = new Composite(container, SWT.FILL);
final GridLayout headerLayout = GridLayoutFactory.fillDefaults().create();
header.setLayout(headerLayout);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(header);
header.setBackground(rightPanel.getBackground());
final Label label = new Label(header, SWT.WRAP);
label.setText("Details"); //$NON-NLS-1$
detailsFont = new Font(label.getDisplay(), getDefaultFontName(label), 10, SWT.BOLD);
label.setFont(detailsFont);
label.setForeground(getTitleColor(parent));
label.setBackground(header.getBackground());
rightPanelContainerComposite = new Composite(container, SWT.FILL);
rightPanelContainerComposite.setLayout(GridLayoutFactory.fillDefaults().create());
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true)
.applyTo(rightPanelContainerComposite);
rightPanelContainerComposite.setBackground(rightPanel.getBackground());
rightPanel.setContent(container);
rightPanel.layout();
container.layout();
final Point point = container.computeSize(SWT.DEFAULT, SWT.DEFAULT);
rightPanel.setMinSize(point);
return rightPanel;
}
@Override
protected String getDefaultFontName(Control control) {
return control.getDisplay().getSystemFont().getFontData()[0].getName();
}
private List<Action> readToolbarActions(EObject modelElement, final EditingDomain editingDomain) {
final List<Action> actions = new ArrayList<Action>();
final IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
if (extensionRegistry == null) {
return actions;
}
if (!VView.class.isInstance(modelElement)) {
return actions;
}
final VView view = (VView) modelElement;
final IConfigurationElement[] controls = extensionRegistry
.getConfigurationElementsFor("org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.masterDetailActions"); //$NON-NLS-1$
for (final IConfigurationElement e : controls) {
try {
final String location = e.getAttribute("location"); //$NON-NLS-1$
if (!location.equals("toolbar")) { //$NON-NLS-1$
continue;
}
final String label = e.getAttribute("label"); //$NON-NLS-1$
final String imagePath = e.getAttribute("imagePath"); //$NON-NLS-1$
final MasterDetailAction command = (MasterDetailAction) e.createExecutableExtension("command"); //$NON-NLS-1$
final Action newAction = new Action() {
@Override
public void run() {
super.run();
command.execute(view);
}
};
newAction.setImageDescriptor(ImageDescriptor.createFromURL(FrameworkUtil.getBundle(command.getClass())
.getResource(imagePath)));
newAction.setText(label);
actions.add(newAction);
} catch (final CoreException e1) {
e1.printStackTrace();
}
}
return actions;
}
private void addDragAndDropSupport(final EObject modelElement, final TreeViewer treeViewer,
EditingDomain editingDomain) {
final int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
final Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance() };
treeViewer.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(treeViewer));
final EditingDomainViewerDropAdapter editingDomainViewerDropAdapter = new EditingDomainViewerDropAdapter(
editingDomain,
treeViewer);
treeViewer.addDropSupport(dndOperations, transfers, editingDomainViewerDropAdapter);
}
/**
* @param treeViewer
* @param editingDomain
*/
private void fillContextMenu(final TreeViewer treeViewer, final EditingDomain editingDomain) {
final ChildrenDescriptorCollector childrenDescriptorCollector = new ChildrenDescriptorCollector();
final List<MasterDetailAction> menuActions = readMasterDetailActions();
final MenuManager menuMgr = new MenuManager();
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new MasterTreeContextMenuListener(editingDomain, treeViewer,
childrenDescriptorCollector, menuActions));
final Menu menu = menuMgr.createContextMenu(treeViewer.getControl());
treeViewer.getControl().setMenu(menu);
}
/**
* Returns a list of all {@link MasterDetailAction MasterDetailActions} which shall be displayed in the context menu
* of the master treeviewer.
*
* @return the actions
*/
protected List<MasterDetailAction> readMasterDetailActions() {
final List<MasterDetailAction> commands = new ArrayList<MasterDetailAction>();
final IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
if (extensionRegistry == null) {
return commands;
}
final IConfigurationElement[] controls = extensionRegistry
.getConfigurationElementsFor("org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.masterDetailActions"); //$NON-NLS-1$
for (final IConfigurationElement e : controls) {
try {
final String location = e.getAttribute("location"); //$NON-NLS-1$
if (!location.equals("menu")) { //$NON-NLS-1$
continue;
}
final String label = e.getAttribute("label"); //$NON-NLS-1$
final String imagePath = e.getAttribute("imagePath"); //$NON-NLS-1$
final MasterDetailAction command = (MasterDetailAction) e.createExecutableExtension("command"); //$NON-NLS-1$
command.setLabel(label);
command.setImagePath(imagePath);
command.setTreeViewer(treeViewer);
commands.add(command);
} catch (final CoreException ex) {
Activator.getDefault().getLog().log(
new Status(IStatus.ERROR, Activator.getDefault().getBundle().getSymbolicName(),
ex.getMessage(), ex));
}
}
return commands;
}
/**
* @param manager The menu manager responsible for the context menu
* @param descriptors The menu items to be added
* @param domain The editing domain of the current EObject
* @param eObject The model element
*/
private void fillContextMenu(IMenuManager manager, Collection<?> descriptors, final EditingDomain domain,
final EObject eObject) {
for (final Object descriptor : descriptors) {
final CommandParameter cp = (CommandParameter) descriptor;
if (!CommandParameter.class.isInstance(descriptor)) {
continue;
}
if (cp.getEReference() == null) {
continue;
}
if (!cp.getEReference().isMany() && eObject.eIsSet(cp.getEStructuralFeature())) {
continue;
} else if (cp.getEReference().isMany() && cp.getEReference().getUpperBound() != -1
&& cp.getEReference().getUpperBound() <= ((List<?>) eObject.eGet(cp.getEReference())).size()) {
continue;
}
manager.add(new CreateChildAction(domain, new StructuredSelection(eObject), descriptor) {
@Override
public void run() {
super.run();
final EReference reference = ((CommandParameter) descriptor).getEReference();
// if (!reference.isContainment()) {
// domain.getCommandStack().execute(
// AddCommand.create(domain, eObject.eContainer(), null, cp.getEValue()));
// }
domain.getCommandStack().execute(
AddCommand.create(domain, eObject, reference, cp.getEValue()));
}
});
}
}
/**
* @param editingDomain
* @param manager
* @param selection
*/
private void addDeleteActionToContextMenu(final EditingDomain editingDomain, final IMenuManager manager,
final IStructuredSelection selection) {
final Action deleteAction = new Action() {
@Override
public void run() {
super.run();
DeleteService deleteService = getViewModelContext().getService(DeleteService.class);
if (deleteService == null) {
/*
* #getService(Class<?>) will report to the reportservice if it could not be found
* Use Default
*/
deleteService = new EMFDeleteServiceImpl();
}
deleteService.deleteElements(selection.toList());
treeViewer.setSelection(new StructuredSelection(getViewModelContext().getDomainModel()));
}
};
final String deleteImagePath = "icons/delete.png";//$NON-NLS-1$
deleteAction.setImageDescriptor(ImageDescriptor.createFromURL(Activator.getDefault()
.getBundle()
.getResource(deleteImagePath)));
deleteAction.setText("Delete"); //$NON-NLS-1$
manager.add(deleteAction);
}
/**
* Allows to manipulate the view context for the selected element that is about to be rendered.
*
* @param viewContext the view context.
*/
protected void manipulateViewContext(ViewModelContext viewContext) {
// do nothing
}
/**
*
* @author Anas Chakfeh
* This class is responsible for handling selection changed events which happen on the tree
*
*/
private class TreeMasterViewSelectionListener implements ISelectionChangedListener {
/**
* Adapter which listens to changes and delegates the notification to other EObjects.
*
* @author Eugen Neufeld
*
*/
private final class MultiEditAdapter extends AdapterImpl {
private final EObject dummy;
private final Set<EObject> selectedEObjects;
private MultiEditAdapter(EObject dummy, Set<EObject> selectedEObjects) {
this.dummy = dummy;
this.selectedEObjects = selectedEObjects;
}
@Override
public void notifyChanged(Notification notification) {
final EditingDomain editingDomain = AdapterFactoryEditingDomain
.getEditingDomainFor(getViewModelContext().getDomainModel());
if (dummy.eClass().getEAllAttributes().contains(notification.getFeature())) {
final CompoundCommand cc = new CompoundCommand();
for (final EObject selected : selectedEObjects) {
Command command = null;
switch (notification.getEventType()) {
case Notification.SET:
command = SetCommand.create(editingDomain, selected,
notification.getFeature(), notification.getNewValue());
break;
case Notification.UNSET:
command = SetCommand.create(editingDomain, selected,
notification.getFeature(), SetCommand.UNSET_VALUE);
break;
case Notification.ADD:
case Notification.ADD_MANY:
command = AddCommand.create(editingDomain, selected,
notification.getFeature(), notification.getNewValue());
break;
case Notification.REMOVE:
case Notification.REMOVE_MANY:
command = DeleteCommand.create(editingDomain, notification.getOldValue());
break;
default:
continue;
}
cc.append(command);
}
editingDomain.getCommandStack().execute(cc);
}
}
}
private Composite childComposite;
private boolean currentDetailViewOriginalReadonly;
@Override
public void selectionChanged(SelectionChangedEvent event) {
final IStructuredSelection selection = (IStructuredSelection) event.getSelection();
final Object treeSelected = getSelection(selection);
final Object selected = treeSelected == null ? treeSelected : manipulateSelection(treeSelected);
if (selected instanceof EObject) {
try {
if (childComposite != null) {
childComposite.dispose();
cleanCustomOnSelectionChange();
}
childComposite = createComposite();
final Object root = manipulateSelection(((RootObject) ((TreeViewer) event.getSource()).getInput())
.getRoot());
final VElement viewModel = getViewModelContext().getViewModel();
final VViewModelProperties properties = ViewModelPropertiesHelper
.getInhertitedPropertiesOrEmpty(viewModel);
properties.addNonInheritableProperty(DETAIL_KEY, true);
final boolean rootSelected = selected.equals(root);
if (rootSelected) {
properties.addNonInheritableProperty(ROOT_KEY, true);
}
VView view = null;
if (rootSelected) {
view = getVElement().getDetailView();
}
if (view == null || view.getChildren().isEmpty()) {
view = ViewProviderHelper.getView((EObject) selected, properties);
}
currentDetailViewOriginalReadonly = view.isReadonly();
final ReferenceService referenceService = getViewModelContext().getService(
ReferenceService.class);
ViewModelContext childContext;
// we have a multi selection, the multi edit is enabled and the multi selection is valid
if (getViewModelContext().getContextValue(ENABLE_MULTI_EDIT) == Boolean.TRUE
&& selection.size() > 1
&& selected != getSelection(new StructuredSelection(selection.getFirstElement()))) {
childContext = ViewModelContextFactory.INSTANCE.createViewModelContext(view, (EObject) selected,
new TreeMasterDetailReferenceService(referenceService));
} else {
childContext = getViewModelContext().getChildContext((EObject) selected,
getVElement(), view, new TreeMasterDetailReferenceService(referenceService));
}
childContext.getViewModel()
.setReadonly(!getVElement().isEffectivelyEnabled() || getVElement().isEffectivelyReadonly()
|| currentDetailViewOriginalReadonly);
// visible does not make any sense
manipulateViewContext(childContext);
ECPSWTViewRenderer.INSTANCE.render(childComposite, childContext);
relayoutDetail();
} catch (final ECPRendererException e) {
Activator
.getDefault()
.getReportService()
.report(new StatusReport(
new Status(IStatus.ERROR, Activator.getDefault().getBundle().getSymbolicName(), e
.getMessage(), e)));
}
}
}
private Object getSelection(IStructuredSelection selection) {
Object treeSelected = selection != null ? selection.getFirstElement() : null;
if (getViewModelContext().getContextValue(ENABLE_MULTI_EDIT) == Boolean.TRUE
&& treeSelected instanceof EObject && selection.size() > 1) {
boolean allOfSameType = true;
final EObject dummy = EcoreUtil.create(((EObject) treeSelected).eClass());
final Iterator iterator = selection.iterator();
final Set<EObject> selectedEObjects = new LinkedHashSet<EObject>();
while (iterator.hasNext()) {
final EObject eObject = (EObject) iterator.next();
allOfSameType &= eObject.eClass() == dummy.eClass();
if (allOfSameType) {
for (final EAttribute attribute : dummy.eClass().getEAllAttributes()) {
if (eObject == treeSelected) {
dummy.eSet(attribute, eObject.eGet(attribute));
} else if (dummy.eGet(attribute) != null
&& !dummy.eGet(attribute).equals(eObject.eGet(attribute))) {
dummy.eUnset(attribute);
}
}
selectedEObjects.add(eObject);
} else {
break;
}
}
if (allOfSameType) {
treeSelected = dummy;
dummy.eAdapters().add(new MultiEditAdapter(dummy, selectedEObjects));
}
}
return treeSelected;
}
private Composite createComposite() {
final Composite parent = getDetailContainer();
final Composite composite = new Composite(parent, SWT.NONE);
composite.setBackground(parent.getBackground());
final GridLayout gridLayout = GridLayoutFactory.fillDefaults().create();
// gridLayout.marginTop = 7;
// gridLayout.marginLeft = 5;
composite.setLayout(gridLayout);
GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(composite);
// .indent(10, 10)
composite.layout(true, true);
return composite;
}
}
/**
* Returns the composite for the detail.
*
* @return the composite
*/
protected Composite getDetailContainer() {
return rightPanelContainerComposite;
}
/**
* Allows to manipulate the selection by returning a specific child.
*
* @param treeSelected the selected element in the tree
* @return the object that should be used as a selection
*/
protected Object manipulateSelection(Object treeSelected) {
return TreeMasterDetailSelectionManipulatorHelper.manipulateSelection(treeSelected);
}
/**
* Gets called after a detail composite was disposed. Allows for further cleanup.
*/
protected void cleanCustomOnSelectionChange() {
// do nothing
}
/**
* Relayouts the detail composite.
*/
protected void relayoutDetail() {
rightPanelContainerComposite.layout();
final Point point = container.computeSize(SWT.DEFAULT, SWT.DEFAULT);
rightPanel.setMinSize(point);
}
/**
* The label provider used for the detail tree.
*
* @author jfaltermeier
*
*/
private class TreeMasterDetailLabelProvider extends AdapterFactoryLabelProvider {
TreeMasterDetailLabelProvider(AdapterFactory adapterFactory) {
super(adapterFactory);
}
@Override
public Image getImage(Object object) {
final Image image = super.getImage(object);
if (!EObject.class.isInstance(object)) {
return image;
}
return getValidationOverlay(image, (EObject) object);
}
protected Image getValidationOverlay(Image image, final EObject object) {
// final Integer severity = validationResultCacheTree.getCachedValue(object);
final VDiagnostic vDiagnostic = getVElement().getDiagnostic();
int highestSeverity = Diagnostic.OK;
if (vDiagnostic != null) {
for (final Diagnostic diagnostic : vDiagnostic.getDiagnostics(object)) {
if (diagnostic.getSeverity() > highestSeverity) {
highestSeverity = diagnostic.getSeverity();
}
}
}
final ImageDescriptor overlay = SWTValidationHelper.INSTANCE
.getValidationOverlayDescriptor(highestSeverity);
if (overlay == null) {
return image;
}
final OverlayImageDescriptor imageDescriptor = new OverlayImageDescriptor(image, overlay,
OverlayImageDescriptor.LOWER_RIGHT);
final Image resultImage = imageDescriptor.createImage();
return resultImage;
}
}
@Override
protected void applyEnable() {
// Re-select the current selection to enforce re-rendering the detail.
treeViewer.setSelection(new StructuredSelection(treeViewer.getStructuredSelection().getFirstElement()));
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#applyValidation()
* @since 1.14
*/
@Override
protected void applyValidation(final VDiagnostic oldDia, final VDiagnostic newDia) {
super.applyValidation();
if (treeViewer == null) {
return;
}
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (treeViewer.getTree().isDisposed()) {
return;
}
updateTree(oldDia, newDia);
}
});
}
private void updateTree(VDiagnostic oldDia, VDiagnostic newDia) {
final List<Object> diff = new ArrayList<Object>();
if (newDia != null) {
diff.addAll(newDia.getDiagnostics());
}
if (oldDia != null) {
diff.removeAll(oldDia.getDiagnostics());
}
final List<Object> diff2 = new ArrayList<Object>();
if (oldDia != null) {
diff2.addAll(oldDia.getDiagnostics());
}
if (newDia != null) {
diff2.removeAll(newDia.getDiagnostics());
}
diff.addAll(diff2);
final Set<Object> toUpdate = new LinkedHashSet<Object>();
final ITreeContentProvider provider = ITreeContentProvider.class.cast(treeViewer.getContentProvider());
for (final Object o : diff) {
final EObject toAdd = (EObject) Diagnostic.class.cast(o).getData().get(0);
toUpdate.add(toAdd);
Object parent = provider.getParent(toAdd);
while (EObject.class.isInstance(parent)) {
toUpdate.add(parent);
parent = provider.getParent(parent);
}
}
if (toUpdate.isEmpty() && !(oldDia.getDiagnostics().isEmpty() && newDia.getDiagnostics().isEmpty())) {
treeViewer.refresh();
return;
}
treeViewer.update(toUpdate.toArray(), null);
}
/**
* reference service for the detail pane of a tree master detail view.
* It delegates calls to a root reference service, but opens new element in the detail pane.
*
* @author Jonas
*
*/
private class TreeMasterDetailReferenceService implements ReferenceService {
private final ReferenceService delegate;
TreeMasterDetailReferenceService(ReferenceService delegate) {
this.delegate = delegate;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#instantiate(org.eclipse.emf.ecp.view.spi.context.ViewModelContext)
*/
@Override
public void instantiate(ViewModelContext context) {
// no op
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#dispose()
*/
@Override
public void dispose() {
// no op
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#getPriority()
*/
@Override
public int getPriority() {
if (delegate == null) {
return 0;
}
return delegate.getPriority() - 1;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.edit.spi.ReferenceService#addNewModelElements(org.eclipse.emf.ecore.EObject,
* org.eclipse.emf.ecore.EReference)
*/
@Override
public void addNewModelElements(EObject eObject, EReference eReference) {
addNewModelElements(eObject, eReference, true);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.edit.spi.ReferenceService#addNewModelElements(org.eclipse.emf.ecore.EObject,
* org.eclipse.emf.ecore.EReference)
*/
@Override
public Optional<EObject> addNewModelElements(EObject eObject, EReference eReference, boolean openInNewContext) {
if (delegate == null) {
return Optional.empty();
}
return delegate.addNewModelElements(eObject, eReference, openInNewContext);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.edit.spi.ReferenceService#addExistingModelElements(org.eclipse.emf.ecore.EObject,
* org.eclipse.emf.ecore.EReference)
*/
@Override
public void addExistingModelElements(EObject eObject, EReference eReference) {
if (delegate == null) {
return;
}
delegate.addExistingModelElements(eObject, eReference);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.edit.spi.ReferenceService#openInNewContext(org.eclipse.emf.ecore.EObject)
*/
@Override
public void openInNewContext(EObject eObject) {
treeViewer.setSelection(new StructuredSelection(eObject), true);
final ISelection selection = treeViewer.getSelection();
if (!selection.isEmpty()) {
return;
}
if (delegate == null) {
return;
}
delegate.openInNewContext(eObject);
}
}
}