Skip to content

Content of file SectionNodeSWTRenderer.java

/*******************************************************************************
 * 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 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:
 * Johannes Faltermeier - initial API and implementation
 ******************************************************************************/
package org.eclipse.emf.ecp.view.spi.section.swt;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.emf.databinding.edit.EMFEditObservables; 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.VElement; import org.eclipse.emf.ecp.view.spi.model.VViewPackage; import org.eclipse.emf.ecp.view.spi.section.model.VSection; import org.eclipse.emf.ecp.view.spi.section.model.VSectionPackage; import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport; import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.domain.EditingDomain; 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.EMFFormsNoRendererException; 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.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.events.IExpansionListener; import org.eclipse.ui.forms.widgets.ExpandableComposite; /** * Renderer for {@link VSection} with child items. * * @author jfaltermeier * */ public class SectionNodeSWTRenderer extends AbstractSectionSWTRenderer { /** * @param vElement the view model element to be rendered * @param viewContext the view context * @param reportService the {@link ReportService} * @param viewTemplateProvider the {@link VTViewTemplateProvider} * @since 1.18 */ @Inject public SectionNodeSWTRenderer(VSection vElement, ViewModelContext viewContext, ReportService reportService, VTViewTemplateProvider viewTemplateProvider) { super(vElement, viewContext, reportService, viewTemplateProvider); } private Set<AbstractSectionSWTRenderer> childRenderers; private SWTGridDescription rendererGridDescription; private ModelChangeListener listener; private ExpandableComposite expandableComposite; @Override protected void preInit() { super.preInit(); listener = new ModelChangeListener() { @Override public void notifyChange(ModelChangeNotification notification) { if (notification.getRawNotification().isTouch()) { return; } if (notification.getNotifier() != getVElement()) { return; } if (notification.getStructuralFeature() == VSectionPackage.eINSTANCE .getSection_Collapsed()) { handleCollapseState(); } } }; getViewModelContext().registerViewChangeListener(listener); } @Override public SWTGridDescription getGridDescription( SWTGridDescription gridDescription) { rendererGridDescription = new SWTGridDescription(); childRenderers = new LinkedHashSet<AbstractSectionSWTRenderer>(); /* get griddescriptions from child sections */ final List<SWTGridDescription> childGridDescriptions = new ArrayList<SWTGridDescription>(); for (final VSection item : getVElement().getChildItems()) { AbstractSWTRenderer<?> itemRenderer; try { itemRenderer = getEMFFormsRendererFactory() .getRendererInstance(item, getViewModelContext()); } catch (final EMFFormsNoRendererException ex) { getReportService().report(new RenderingFailedReport(ex)); continue; } final SWTGridDescription itemGridDescription = itemRenderer .getGridDescription(GridDescriptionFactory.INSTANCE .createEmptyGridDescription()); childRenderers.add((AbstractSectionSWTRenderer) itemRenderer); childGridDescriptions.add(itemGridDescription); } /* compute required column count based on self and children */ final int selfColumns = 1 + getVElement().getChildren().size(); int columns = selfColumns; for (final SWTGridDescription childGridDescription : childGridDescriptions) { columns = childGridDescription.getColumns() > columns ? childGridDescription.getColumns() : columns; } /* create grid description for this renderer */ rendererGridDescription.setColumns(columns); final List<SWTGridCell> gridCells = new ArrayList<SWTGridCell>(); int emptyCellColumnIndicator = -1; /* add self */ int row = 0; /* label */ final Optional<Integer> labelWidth = getLabelWidth(); Point prefSize; if (labelWidth.isPresent()) { prefSize = new Point(labelWidth.get(), SWT.DEFAULT); } else { prefSize = new Point(SWT.DEFAULT, SWT.DEFAULT); } int curSelfColumn = 0; gridCells.add(createGridCell(row, curSelfColumn++, this, prefSize)); /* empty columns */ final int emptyColumns = columns - selfColumns; for (int i = 0; i < emptyColumns; i++) { gridCells.add(createGridCell(row, emptyCellColumnIndicator--, this)); } /* regular columns */ for (int columnToAdd = 0; columnToAdd < getVElement().getChildren().size(); columnToAdd++) { gridCells.add(createGridCell(row, curSelfColumn++, this)); } row += 1; /* add children */ for (final SWTGridDescription childGridDescription : childGridDescriptions) { final SWTGridCell[][] sortedChildGridCells = getArrangedChildGridCells(childGridDescription, columns); for (final SWTGridCell[] rowGridCells : sortedChildGridCells) { /* There is always at least one column (index) */ final int currentRow = rowGridCells[0].getRow() + row; final AbstractSWTRenderer<?> renderer = rowGridCells[0].getRenderer(); for (int i = 0; i < rowGridCells.length; i++) { final SWTGridCell swtGridCell = rowGridCells[i]; if (swtGridCell != null) { gridCells.add( createGridCell( currentRow, swtGridCell.getColumn(), swtGridCell.getRenderer(), swtGridCell.getPreferredSize())); } else { /* create empty column */ gridCells.add(createGridCell(currentRow, emptyCellColumnIndicator--, renderer)); } } } row += childGridDescription.getRows(); } rendererGridDescription.setRows(row); rendererGridDescription.setGrid(gridCells); return rendererGridDescription; } private SWTGridCell[][] getArrangedChildGridCells(SWTGridDescription childGridDescription, int columns) { final SWTGridCell[][] result = new SWTGridCell[childGridDescription.getRows()][columns]; for (final SWTGridCell swtGridCell : childGridDescription.getGrid()) { if (swtGridCell.getColumn() < 0) { continue; } result[swtGridCell.getRow()][swtGridCell.getColumn()] = swtGridCell; } for (final SWTGridCell[] columnArray : result) { shiftElementsToEndOfArrayButFirstElement(columnArray); } return result; } private static void shiftElementsToEndOfArrayButFirstElement(SWTGridCell[] columnArray) { final int length = columnArray.length; for (int i = length - 1; i >= 0; i--) { if (columnArray[i] == null) { final int index = getIndexToMove(columnArray, i); if (index == -1) { return; } columnArray[i] = columnArray[index]; columnArray[index] = null; } else { return; } } } private static int getIndexToMove(SWTGridCell[] columnArray, int index) { for (int i = index - 1; i > 0; i--) { if (columnArray[i] != null) { return i; } } return -1; } private SWTGridCell createGridCell(int row, int column, AbstractSWTRenderer<? extends VElement> renderer) { final SWTGridCell gridCell = new SWTGridCell(row, column, renderer); gridCell.setVerticalFill(false); gridCell.setVerticalGrab(false); if (column == 3) { gridCell.setHorizontalGrab(true); } else { gridCell.setHorizontalGrab(false); } return gridCell; } private SWTGridCell createGridCell(int row, int column, AbstractSWTRenderer<? extends VElement> renderer, Point prefSize) { final SWTGridCell gridCell = createGridCell(row, column, renderer); gridCell.setPreferredSize(prefSize); return gridCell; } @Override protected Control createFirstColumn(Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().numColumns(1) .extendedMargins(computeLeftMargin(), 0, 0, 0) .applyTo(composite); setExpandableComposite(new ExpandableComposite( composite, SWT.NONE, ExpandableComposite.TWISTIE)); getExpandableComposite().setExpanded(!getVElement().isCollapsed()); final EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(getVElement()); final IObservableValue modelLabelValue = EMFEditObservables.observeValue( editingDomain, getVElement(), VViewPackage.eINSTANCE.getElement_Label()); final String text = "text"; //$NON-NLS-1$ final WritableValue value = new WritableValue(text, String.class); final IObservableValue textObservable = PojoProperties.value(ExpandableComposite.class, text, String.class) .observe(expandableComposite); getDataBindingContext().bindValue(textObservable, modelLabelValue); initExpandableComposite(getExpandableComposite()); final IObservableValue modelTooltipValue = EMFEditObservables.observeValue( editingDomain, getVElement(), VViewPackage.eINSTANCE.getHasTooltip_Tooltip()); final IObservableValue targetTooltipValue = new ExpandableCompositeTooltipProperty() .observe(getExpandableComposite()); getDataBindingContext().bindValue(targetTooltipValue, modelTooltipValue); return composite; } private void initExpandableComposite(ExpandableComposite expandableComposite) { expandableComposite.addExpansionListener(new IExpansionListener() { @Override public void expansionStateChanging(ExpansionEvent e) { } @Override public void expansionStateChanged(ExpansionEvent e) { getVElement().setCollapsed(!e.getState()); } }); } @Override protected void adjustLayoutData(boolean vis) { super.adjustLayoutData(vis); for (final AbstractSectionSWTRenderer childRenderer : childRenderers) { boolean visible = vis; if (getVElement().isCollapsed()) { visible = false; } childRenderer.adjustLayoutData(visible); } } @Override protected void applyEnable() { getExpandableComposite().setEnabled(getVElement().isEffectivelyEnabled()); } @Override protected void dispose() { getViewModelContext().unregisterViewChangeListener(listener); super.dispose(); } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.view.spi.section.swt.AbstractSectionSWTRenderer#initCollapseState() * @since 1.6 */ @Override protected void initCollapseState() { /* top root gets current width as width hint so that further resizes will keep the column width intact */ final Iterator<Control> iterator = getControls().values().iterator(); while (iterator.hasNext()) { final Control control = iterator.next(); final int width = control.getSize().x; final Object layoutData = control.getLayoutData(); if (GridData.class.isInstance(layoutData)) { final GridData gridData = (GridData) layoutData; if (gridData == null) { continue; } gridData.widthHint = width; if (iterator.hasNext()) { continue; } gridData.grabExcessHorizontalSpace = true; } } handleCollapseState(); } private void handleCollapseState() { for (final AbstractSectionSWTRenderer childRenderer : childRenderers) { childRenderer.adjustLayoutData(!getVElement() .isCollapsed()); } getControls().values().iterator().next().getParent() .layout(false); getExpandableComposite().setExpanded(!getVElement() .isCollapsed()); } /** * @return the expandableComposite * @since 1.13 */ protected ExpandableComposite getExpandableComposite() { return expandableComposite; } /** * @param expandableComposite the expandableComposite to set * @since 1.13 */ protected void setExpandableComposite(ExpandableComposite expandableComposite) { this.expandableComposite = expandableComposite; } }