blob: 118c3822b1705e2102a66c8792ba4b205d438913 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2016 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 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:
* Eugen Neufeld - initial API and implementation
******************************************************************************/
package org.eclipse.emfforms.internal.swt.categorization.expandbar;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.inject.Inject;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.common.notify.Adapter;
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.common.util.EList;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
import org.eclipse.emf.ecp.view.spi.categorization.model.VAbstractCategorization;
import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorizableElement;
import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorization;
import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorizationElement;
import org.eclipse.emf.ecp.view.spi.categorization.model.VCategory;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
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.VViewPackage;
import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emfforms.spi.common.report.AbstractReport;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer;
import org.eclipse.emfforms.spi.swt.core.EMFFormsNoRendererException;
import org.eclipse.emfforms.spi.swt.core.EMFFormsRendererFactory;
import org.eclipse.emfforms.spi.swt.core.SWTDataElementIdHelper;
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.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.widgets.MarkupValidator;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.ExpandBar;
import org.eclipse.swt.widgets.ExpandItem;
import org.eclipse.swt.widgets.Table;
/**
* A VCategorizationElement renderer that renders the categories as expandbar items.
* This renderer does only work if there are only VCategorization items on the root and each VCategorization item
* contains only VCategory items.
*
* @author Eugen Neufeld
*
*/
@SuppressWarnings("restriction")
public class ExpandBarCategorizationElementRenderer extends AbstractSWTRenderer<VCategorizationElement> {
private static final String VALIDATION_ERRORS = "validationErrors"; //$NON-NLS-1$
private SWTGridDescription gridDescription;
private final EMFFormsRendererFactory emfFormsRendererFactory;
private ScrolledComposite editorComposite;
private AdapterFactoryLabelProvider labelProvider;
private ExpandBar expandBar;
private final EMFDataBindingContext dbc;
private ComposedAdapterFactory adapterFactory;
private AdapterFactoryContentProvider contentProvider;
/**
* Default constructor.
*
* @param vElement the view model element to be rendered
* @param viewContext the view context
* @param reportService the {@link ReportService}
* @param emfFormsRendererFactory The {@link EMFFormsRendererFactory}
* @since 1.9
*/
@Inject
public ExpandBarCategorizationElementRenderer(VCategorizationElement vElement, ViewModelContext viewContext,
ReportService reportService, EMFFormsRendererFactory emfFormsRendererFactory) {
super(vElement, viewContext, reportService);
this.emfFormsRendererFactory = emfFormsRendererFactory;
dbc = new EMFDataBindingContext();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#getGridDescription(org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription)
*/
@Override
public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
if (this.gridDescription == null) {
this.gridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
}
return this.gridDescription;
}
/**
* {@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 {
adapterFactory = new ComposedAdapterFactory(
ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
final EList<VAbstractCategorization> categorizations = getVElement().getCategorizations();
if (categorizations.size() == 1 && categorizations.get(0) instanceof VCategory) {
final VElement child = categorizations.get(0);
AbstractSWTRenderer<VElement> renderer;
try {
renderer = getEMFFormsRendererFactory().getRendererInstance(child,
getViewModelContext());
} catch (final EMFFormsNoRendererException ex) {
getReportService().report(new AbstractReport(ex));
return null;
}
final Control render = renderer.render(cell, parent);
renderer.finalizeRendering(parent);
SWTDataElementIdHelper.setElementIdDataWithSubId(render, getVElement(), "vcategory", getViewModelContext()); //$NON-NLS-1$
return render;
}
Composite expandBar;
final Object detailPane = getViewModelContext().getContextValue("detailPane"); //$NON-NLS-1$
if (detailPane != null && Composite.class.isInstance(detailPane)) {
expandBar = createExpandBarMaster(parent);
editorComposite = createdEditorPane(Composite.class.cast(detailPane));
Composite.class.cast(detailPane).layout();
selectFirstEntry();
return expandBar;
}
final SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL);
SWTDataElementIdHelper.setElementIdDataWithSubId(sashForm, getVElement(), "sash", getViewModelContext()); //$NON-NLS-1$
GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(sashForm);
expandBar = createExpandBarMaster(sashForm);
editorComposite = createdEditorPane(sashForm);
sashForm.setWeights(new int[] { 1, 3 });
selectFirstEntry();
return sashForm;
}
private void selectFirstEntry() {
if (expandBar.getItemCount() != 0) {
final TableViewer tableViewer = TableViewer.class
.cast(expandBar.getItem(0).getData("tableViewer")); //$NON-NLS-1$
tableViewer.setSelection(new StructuredSelection(tableViewer.getElementAt(0)));
}
}
private Composite createExpandBarMaster(final Composite parent) {
labelProvider = new AdapterFactoryLabelProvider(adapterFactory) {
@Override
public Image getImage(Object object) {
return null;
}
@Override
public Image getColumnImage(Object object, int columnIndex) {
return null;
}
@Override
public String getColumnText(Object object, int columnIndex) {
String result = super.getColumnText(object, columnIndex);
final VDiagnostic diagnostic = VCategory.class.cast(object).getDiagnostic();
if (diagnostic != null && diagnostic.getHighestSeverity() > 0) {
result += String.format(
" <span style='background-color:#%2$s;border-radius: 1em;color:#%3$s;height:1.25em;width:1.25em;display:inline-block;text-align:center'>%1$s</span>", //$NON-NLS-1$
diagnostic.getDiagnostics().size(), SWTValidationHelper.INSTANCE.getValidationColorHEX(
diagnostic.getHighestSeverity(), getVElement(), getViewModelContext()),
getFontColor());
}
return String.format("<span style='display:inline-block;margin-left:1.25em;'>%1$s</span>", result); //$NON-NLS-1$
}
};
final Composite expandBarComposite = new Composite(parent, SWT.BORDER);
SWTDataElementIdHelper.setElementIdDataWithSubId(expandBarComposite, getVElement(), "expandBarComposite", //$NON-NLS-1$
getViewModelContext());
expandBarComposite.setLayout(new GridLayout());
expandBar = new ExpandBar(expandBarComposite, SWT.V_SCROLL);
SWTDataElementIdHelper.setElementIdDataWithSubId(expandBar, getVElement(), "expandBar", getViewModelContext()); //$NON-NLS-1$
expandBar.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
int destroyedItems = 0;
for (final VAbstractCategorization categorization : getVElement().getCategorizations()) {
final ExpandItem item = createExpandItem(adapterFactory, categorization, destroyedItems);
item.setExpanded(true);
categorization.eAdapters().add(new CategorizationVisibilityAdapter(categorization, item));
if (!categorization.isVisible()) {
item.dispose();
destroyedItems++;
}
}
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true)
.hint(SWT.DEFAULT, 5000) // arbitrary high height that won't be reached
.applyTo(expandBar);
return expandBarComposite;
}
private ExpandItem createExpandItem(final ComposedAdapterFactory adapterFactory,
final VAbstractCategorization categorization, int destroyedItems) {
final ExpandItem item = new ExpandItem(expandBar, SWT.NONE,
getVElement().getCategorizations().indexOf(categorization) - destroyedItems);
SWTDataElementIdHelper.setElementIdDataWithSubId(item, categorization, "expandItem", getViewModelContext()); //$NON-NLS-1$
item.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
final ISWTObservableValue target = WidgetProperties.text().observe(item);
final IObservableValue modelValue = EMFEditObservables.observeValue(
AdapterFactoryEditingDomain.getEditingDomainFor(categorization), categorization,
VViewPackage.eINSTANCE.getElement_Label());
dbc.bindValue(target, modelValue);
item.setImage(labelProvider.getImage(categorization));
final TableViewer tv = new TableViewer(expandBar, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
tv.getTable().setHeaderVisible(false);
tv.setLabelProvider(labelProvider);
tv.addFilter(new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
return VCategory.class.isInstance(element) && VCategory.class.cast(element).isVisible();
}
});
contentProvider = new AdapterFactoryContentProvider(adapterFactory);
tv.setContentProvider(contentProvider);
tv.setInput(VCategorization.class.cast(categorization));
tv.addSelectionChangedListener(new CustomTableSelectionAdapter());
tv.getTable().setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
tv.getTable().setData(MarkupValidator.MARKUP_VALIDATION_DISABLED, Boolean.TRUE);
item.setControl(tv.getControl());
SWTDataElementIdHelper.setElementIdDataWithSubId(tv.getControl(), categorization, "expandItemContent", //$NON-NLS-1$
getViewModelContext());
item.setHeight(computeHeight(item.getControl()));
if (categorization.getDiagnostic() != null && categorization.getDiagnostic().getHighestSeverity() > 0) {
item.setData(RWT.CUSTOM_VARIANT, getValidationKey(categorization));
}
item.setData("tableViewer", tv); //$NON-NLS-1$
return item;
}
private String getValidationKey(final VAbstractCategorization categorization) {
switch (categorization.getDiagnostic().getHighestSeverity()) {
case Diagnostic.INFO:
return VALIDATION_ERRORS + "_info"; //$NON-NLS-1$
case Diagnostic.WARNING:
return VALIDATION_ERRORS + "_warning"; //$NON-NLS-1$
case Diagnostic.ERROR:
return VALIDATION_ERRORS + "_error"; //$NON-NLS-1$
case Diagnostic.CANCEL:
return VALIDATION_ERRORS + "_cancel"; //$NON-NLS-1$
default:
return VALIDATION_ERRORS;
}
}
private String getFontColor() {
return "ffffff"; //$NON-NLS-1$
}
private int computeHeight(Control control) {
return control.computeSize(SWT.DEFAULT, SWT.DEFAULT).y + 1;
}
private Composite createComposite(Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setBackground(parent.getBackground());
GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).margins(7, 7).applyTo(composite);
GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(composite);
return composite;
}
/**
* Created editor pane.
*
* @param composite the composite
* @return the created editor composite
*/
protected ScrolledComposite createdEditorPane(Composite composite) {
final ScrolledComposite editorComposite = createScrolledComposite(composite);
editorComposite.setExpandHorizontal(true);
editorComposite.setExpandVertical(true);
editorComposite.setShowFocusedControl(true);
SWTDataElementIdHelper.setElementIdDataWithSubId(editorComposite, getVElement(), "editorComposite", //$NON-NLS-1$
getViewModelContext());
GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(editorComposite);
return editorComposite;
}
/**
* Creates the scrolled composite.
*
* @param parent the parent
* @return the scrolled composite
*/
private ScrolledComposite createScrolledComposite(Composite parent) {
final ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL
| SWT.BORDER);
scrolledComposite.setShowFocusedControl(true);
scrolledComposite.setExpandVertical(true);
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setBackground(parent.getBackground());
return scrolledComposite;
}
private EMFFormsRendererFactory getEMFFormsRendererFactory() {
return emfFormsRendererFactory;
}
/**
* Adapter to check for visibility changes on categorizations.
*
* @author Eugen Neufeld
*
*/
private final class CategorizationVisibilityAdapter extends AdapterImpl {
private final VAbstractCategorization categorization;
private ExpandItem catItem;
private CategorizationVisibilityAdapter(VAbstractCategorization categorization, ExpandItem item) {
this.categorization = categorization;
catItem = item;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
*/
@Override
public void notifyChanged(Notification msg) {
super.notifyChanged(msg);
if (msg.getFeature() != VViewPackage.eINSTANCE.getElement_Visible()) {
return;
}
if (!categorization.isVisible()) {
if (catItem.getControl().isFocusControl()) {
expandBar.forceFocus();
}
catItem.dispose();
} else {
int index=Math.min(expandBar.getChildren().length, getVElement().getCategorizations().indexOf(categorization));
catItem = createExpandItem(adapterFactory, categorization,index );
}
}
}
/**
* A custom adapter that resets the selection in the other table viewers and triggers the rendering of the selected
* category.
*
* @author Eugen Neufeld
*
*/
private class CustomTableSelectionAdapter implements ISelectionChangedListener {
private Composite childComposite;
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
@Override
public void selectionChanged(SelectionChangedEvent event) {
for (int i = 0; i < expandBar.getItemCount(); i++) {
final ExpandItem expandItem = expandBar.getItem(i);
final Table table = (Table) expandItem.getControl();
if (TableViewer.class.cast(event.getSource()).getTable() != table) {
table.deselectAll();
}
}
final Object selection = IStructuredSelection.class.cast(event.getSelection()).getFirstElement();
if (childComposite != null) {
childComposite.dispose();
childComposite = null;
}
if (selection == null) {
return;
}
childComposite = createComposite(editorComposite);
childComposite.setBackground(editorComposite.getBackground());
editorComposite.setContent(childComposite);
final VElement child = (VElement) selection;
try {
AbstractSWTRenderer<VElement> renderer;
try {
renderer = getEMFFormsRendererFactory().getRendererInstance(child, getViewModelContext());
} catch (final EMFFormsNoRendererException ex) {
getReportService().report(new AbstractReport(ex));
return;
}
final Control render = renderer.render(
renderer.getGridDescription(
GridDescriptionFactory.INSTANCE.createEmptyGridDescription()).getGrid().get(0),
childComposite);
renderer.finalizeRendering(childComposite);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true)
.minSize(SWT.DEFAULT, 200)
.applyTo(render);
getVElement().setCurrentSelection((VCategorizableElement) child);
} catch (final NoRendererFoundException e) {
getReportService().report(new RenderingFailedReport(e));
} catch (final NoPropertyDescriptorFoundExeption e) {
getReportService().report(new RenderingFailedReport(e));
}
childComposite.layout();
final Point point = childComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
editorComposite.setMinSize(point);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#applyValidation()
*/
@Override
protected void applyValidation() {
super.applyValidation();
labelProvider.fireLabelProviderChanged();
for (int i = 0; i < expandBar.getItemCount(); i++) {
final ExpandItem expandItem = expandBar.getItem(i);
final VAbstractCategorization categorization = getVElement().getCategorizations().get(i);
expandItem.setImage(labelProvider.getImage(categorization));
if (categorization.getDiagnostic() != null && categorization.getDiagnostic().getHighestSeverity() > 0) {
expandItem.setData(RWT.CUSTOM_VARIANT,
getValidationKey(categorization));
} else {
expandItem.setData(RWT.CUSTOM_VARIANT, null);
}
}
}
@Override
protected void dispose() {
if (dbc != null) {
dbc.dispose();
}
if (adapterFactory != null) {
adapterFactory.dispose();
}
if (labelProvider != null) {
labelProvider.dispose();
}
if (contentProvider != null) {
contentProvider.dispose();
}
for (final VAbstractCategorization categorization : getVElement().getCategorizations()) {
final Set<Adapter> toRemove = new LinkedHashSet<Adapter>();
for (final Adapter adapter : categorization.eAdapters()) {
if (CategorizationVisibilityAdapter.class.isInstance(adapter)) {
toRemove.add(adapter);
}
}
categorization.eAdapters().removeAll(toRemove);
}
super.dispose();
}
}