blob: 1d8c93f77d897eb5cfe071739e3cf97d92052aa4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2014 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:
* Edagr Mueller - initial API and implementation
* Eugen Neufeld - Refactoring
******************************************************************************/
package org.eclipse.emfforms.spi.swt.core;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecp.view.model.common.AbstractRenderer;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
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.VViewPackage;
import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.swt.core.layout.EMFFormsSWTLayoutUtil;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
/**
* Common base class for all SWT specific renderer classes.
*
* A renderer using other renderers to render its contents must call this methods in this order:
*
* <pre>
* {@link #getGridDescription(SWTGridDescription)}
* for each SWTGridCell
* {@link #render(SWTGridCell, Composite)}
* {@link #finalizeRendering(Composite)}
* </pre>
*
* If you don't call {@link #finalizeRendering(Composite)} after the rendering, the automatic disposing of the renderer
* will not work, as well as the initial validation check.
*
* @author Eugen Neufeld
*
* @param <VELEMENT> the actual type of the {@link VElement} to be drawn
* @since 1.2
*/
public abstract class AbstractSWTRenderer<VELEMENT extends VElement> extends AbstractRenderer<VELEMENT> {
/**
* Variant constant for indicating RAP controls.
*/
protected static final String CUSTOM_VARIANT = "org.eclipse.rap.rwt.customVariant"; //$NON-NLS-1$
private ModelChangeListener listener;
private Map<SWTGridCell, Control> controls;
private boolean renderingFinished;
/**
* 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
*/
public AbstractSWTRenderer(final VELEMENT vElement, final ViewModelContext viewContext,
ReportService reportService) {
super(vElement, viewContext, reportService);
}
/**
* Returns the GridDescription for this Renderer.
*
* @param gridDescription the current {@link SWTGridDescription}
* @return the number of controls per row
* @since 1.3
*/
public abstract SWTGridDescription getGridDescription(SWTGridDescription gridDescription);
/**
* Initializes the {@link AbstractSWTRenderer}.
*
* @since 1.6
*/
public final void init() {
preInit();
controls = new LinkedHashMap<SWTGridCell, Control>();
if (getViewModelContext() != null) {
listener = new ModelChangeListener() {
@Override
public void notifyChange(ModelChangeNotification notification) {
if (!renderingFinished) {
return;
}
if (notification.getRawNotification().isTouch()) {
return;
}
// Always apply enable and read-only if it changed because it might have changed for a parent
if (notification.getStructuralFeature() == VViewPackage.eINSTANCE.getElement_Enabled()) {
if (!ignoreEnableOnReadOnly()) {
applyEnable();
}
} else if (notification.getStructuralFeature() == VViewPackage.eINSTANCE.getElement_Readonly()) {
applyReadOnly();
}
if (notification.getNotifier() != getVElement()) {
return;
}
if (notification.getStructuralFeature() == VViewPackage.eINSTANCE.getElement_Visible()) {
applyVisible();
} else if (notification.getStructuralFeature() == VViewPackage.eINSTANCE.getElement_Diagnostic()) {
final VDiagnostic newDia = (VDiagnostic) notification.getRawNotification().getNewValue();
final VDiagnostic oldDia = (VDiagnostic) notification.getRawNotification().getOldValue();
applyValidation(oldDia, newDia);
applyValidation();
}
}
};
getViewModelContext().registerViewChangeListener(listener);
}
getViewModelContext().addContextUser(this);
postInit();
}
/**
* Returns a copy of the {@link GridCell} to {@link Control} map.
*
* @return a copy of the controls map
* @since 1.3
*/
protected final Map<SWTGridCell, Control> getControls() {
if (controls == null) {
return Collections.emptyMap();
}
return new LinkedHashMap<SWTGridCell, Control>(controls);
}
/**
* Use this method to initialize objects which are needed already before rendering.
*
* @since 1.3
*/
protected void preInit() {
}
/**
* Use this method to initialize objects which are needed during rendering.
*
* @since 1.3
*/
protected void postInit() {
}
/**
* Disposes all resources used by the renderer.
* Don't forget to call super.dispose if overwriting this method.
*
* @since 1.3
*/
@Override
protected void dispose() {
if (getViewModelContext() != null) {
getViewModelContext().unregisterViewChangeListener(listener);
}
listener = null;
controls = null;
getViewModelContext().removeContextUser(this);
super.dispose();
}
/**
* Renders the passed {@link VElement}.
*
* @param cell the {@link SWTGridCell} of the control to render
* @param parent the {@link Composite} to render on
* @return the rendered {@link Control}
* @throws NoRendererFoundException this is thrown when a renderer cannot be found
* @throws NoPropertyDescriptorFoundExeption this is thrown when no property descriptor can be found
* @since 1.3
*/
public Control render(final SWTGridCell cell, Composite parent)
throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
Control control = controls.get(cell);
if (control != null) {
return control;
}
control = renderControl(cell, parent);
if (control == null) {
// something went wrong, log
return null;
}
controls.put(cell, control);
// register dispose listener to rerender if disposed
control.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (controls != null) {
controls.remove(cell);
}
}
});
return control;
}
/**
* Called by the framework to initialize listener.
*
* @param parent the parent used during render
* @since 1.3
*/
public void finalizeRendering(Composite parent) {
if (renderingFinished) {
return;
}
renderingFinished = true;
if (!getVElement().isVisible()) {
/* convention is to render visible, so only call apply if we are invisible */
applyVisible();
}
applyReadOnly();
if (!ignoreEnableOnReadOnly()) {
applyEnable();
}
applyValidation();
parent.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent event) {
dispose();
}
});
}
/**
* Returns <code>true</code> when read only will always force control to be disabled.
*
* @return <code>true</code> when read only will always force control to be disabled.
*/
protected boolean ignoreEnableOnReadOnly() {
return getVElement().isEffectivelyReadonly();
}
/**
* Renders the passed {@link VElement}.
*
* @param cell the {@link GridCell} of the control to render
* @param parent the {@link Composite} to render on
* @return the rendered {@link Control}
* @throws NoRendererFoundException this is thrown when a renderer cannot be found
* @throws NoPropertyDescriptorFoundExeption this is thrown when no property descriptor can be found
* @since 1.3
*/
protected abstract Control renderControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
NoPropertyDescriptorFoundExeption;
/**
* Marks a controls as readonly.
*
* @since 1.3
*
*/
protected void applyReadOnly() {
// Do nothing, implement behavior in implementing class if needed
}
/**
* Allows implementers to set a control to enabled.
*
* @since 1.3
*
*/
protected void applyEnable() {
// Do nothing, implement behavior in implementing class if needed
}
/**
* Wraps the call to enable/disable a control.
*
* @param gridCell the {@link SWTGridCell} to enable/disable
* @param control the {@link Control} to enable/disable
* @param enabled true if control should be enabled, false otherwise
* @since 1.3
*/
protected void setControlEnabled(SWTGridCell gridCell, Control control, boolean enabled) {
control.setEnabled(enabled);
}
/**
* Allows implementers to check and set the visibility on the whole result row.
*
* @since 1.3
*
*/
protected void applyVisible() {
final boolean visible = getVElement().isVisible();
/* avoid multiple layout calls by saving the parents which need to be relayouted */
final Set<Composite> parents = new LinkedHashSet<Composite>();
for (final SWTGridCell gridCell : controls.keySet()) {
final Object layoutData = controls.get(gridCell).getLayoutData();
if (GridData.class.isInstance(layoutData)) {
final GridData gridData = (GridData) layoutData;
if (gridData != null) {
gridData.exclude = !visible;
}
}
controls.get(gridCell).setVisible(visible);
parents.add(controls.get(gridCell).getParent());
}
for (final Composite composite : parents) {
EMFFormsSWTLayoutUtil.adjustParentSize(composite.getChildren()[0]);
}
}
/**
* Allows implementers to display the validation state of the control.
* The default implementation does nothing.
*
* @since 1.3
*/
protected void applyValidation() {
}
/**
* Called before the {@link #applyValidation()}. This method allows to create a diff between the old diagnostic and
* the new diagnostic and thus improve the performance of the overlay apply by triggering it only on the relevant
* elements.
*
* @param oldDiagnostic The previous {@link VDiagnostic}
* @param newDiagnostic The current {@link VDiagnostic}
* @since 1.14
*/
protected void applyValidation(VDiagnostic oldDiagnostic, VDiagnostic newDiagnostic) {
}
/**
* @return String the default font name on the system.
* @param control The control to derive the default font name from
*
* @since 1.5
*/
protected String getDefaultFontName(Control control) {
return control.getDisplay().getSystemFont().getFontData()[0].getName();
}
}