org.lunifera.runtime.web.ecview.presentation.vaadin.internal.GridLayoutPresentation.java Source code

Java tutorial

Introduction

Here is the source code for org.lunifera.runtime.web.ecview.presentation.vaadin.internal.GridLayoutPresentation.java

Source

/**
 * Copyright (c) 2011 - 2014, Lunifera GmbH (Gross Enzersdorf), Loetz KG (Heidelberg)
 * 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: 
 *       Florian Pirchner - Initial implementation
 */
package org.lunifera.runtime.web.ecview.presentation.vaadin.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecp.ecview.common.editpart.IElementEditpart;
import org.eclipse.emf.ecp.ecview.common.editpart.IEmbeddableEditpart;
import org.eclipse.emf.ecp.ecview.common.editpart.ILayoutEditpart;
import org.eclipse.emf.ecp.ecview.common.model.core.YEmbeddable;
import org.eclipse.emf.ecp.ecview.extension.model.extension.YAlignment;
import org.eclipse.emf.ecp.ecview.extension.model.extension.YGridLayout;
import org.eclipse.emf.ecp.ecview.extension.model.extension.YGridLayoutCellStyle;
import org.eclipse.emf.ecp.ecview.extension.model.extension.YSpanInfo;
import org.lunifera.runtime.web.ecview.presentation.vaadin.IConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.GridLayout.Area;

/**
 * This presenter is responsible to render a text field on the given layout.
 */
public class GridLayoutPresentation extends AbstractLayoutPresenter<ComponentContainer> {

    private static final Logger LOGGER = LoggerFactory.getLogger(GridLayoutPresentation.class);

    private CssLayout componentBase;
    private GridLayout gridlayout;
    private ModelAccess modelAccess;

    /**
     * The constructor.
     * 
     * @param editpart
     *            The editpart of that presentation.
     */
    public GridLayoutPresentation(IElementEditpart editpart) {
        super((ILayoutEditpart) editpart);
        this.modelAccess = new ModelAccess((YGridLayout) editpart.getModel());
    }

    @Override
    public void add(IEmbeddableEditpart editpart) {
        super.add(editpart);

        refreshUI();
    }

    @Override
    public void remove(IEmbeddableEditpart editpart) {
        super.remove(editpart);

        refreshUI();
    }

    @Override
    public void insert(IEmbeddableEditpart editpart, int index) {
        super.insert(editpart, index);

        refreshUI();
    }

    @Override
    public void move(IEmbeddableEditpart editpart, int index) {
        super.move(editpart, index);

        refreshUI();
    }

    @Override
    protected void doUpdateLocale(Locale locale) {
        // no need to set the locale to the ui elements. Is handled by vaadin
        // internally.

        // update the captions
        applyCaptions();
    }

    /**
     * Applies the labels to the widgets.
     */
    protected void applyCaptions() {

    }

    /**
     * Is called to refresh the UI. The element will be removed from the grid
     * layout and added to it again afterwards.
     */
    protected void refreshUI() {
        if (!isRendered()) {
            return;
        }
        gridlayout.removeAllComponents();

        // create a map containing the style for the embeddable
        //
        Map<YEmbeddable, YGridLayoutCellStyle> yStyles = new HashMap<YEmbeddable, YGridLayoutCellStyle>();
        for (YGridLayoutCellStyle style : modelAccess.getCellStyles()) {
            if (yStyles.containsKey(style.getTarget())) {
                LOGGER.warn("Multiple style for element {}", style.getTarget());
            }
            yStyles.put(style.getTarget(), style);
        }

        // iterate all elements and build the child element
        //
        List<Cell> cells = new ArrayList<Cell>();
        for (IEmbeddableEditpart editPart : getEditpart().getElements()) {
            YEmbeddable yChild = (YEmbeddable) editPart.getModel();
            Cell cell = addChild(editPart, yStyles.get(yChild));
            cells.add(cell);
        }

        // Build a model of rows and columns.
        // Each coordinate (row/column) has an assigned cell. If a cell is
        // spanned,
        // it will be assigned to many coordinates.
        List<Row> rows = new ArrayList<Row>();
        for (int i = 0; i < gridlayout.getRows(); i++) {
            rows.add(new Row(i, gridlayout.getColumns()));
        }
        List<Column> columns = new ArrayList<Column>();
        for (int i = 0; i < gridlayout.getColumns(); i++) {
            columns.add(new Column(i, gridlayout.getRows()));
        }

        for (Cell cell : cells) {
            for (int r = cell.area.getRow1(); r <= cell.area.getRow2(); r++) {
                for (int c = cell.area.getColumn1(); c <= cell.area.getColumn2(); c++) {
                    Row row = rows.get(r);
                    row.addCell(c, cell);
                    Column col = columns.get(c);
                    col.addCell(r, cell);
                }
            }
        }

        boolean expandVerticalFound = false;
        for (Row row : rows) {
            if (row.isShouldExpandVertical()) {
                expandVerticalFound = true;
                gridlayout.setRowExpandRatio(row.getRowindex(), 1.0f);
            }
        }

        boolean expandHorizontalFound = false;
        for (Column col : columns) {
            if (col.isShouldExpandHorizontal()) {
                expandHorizontalFound = true;
                gridlayout.setColumnExpandRatio(col.getColumnindex(), 1.0f);
            }
        }

        // handle packaging - therefore a new row / column is added and set to
        // expandRatio = 1.0f. This will cause the
        // last row / column to grab excess space.
        // If there is already a row / column that is expanded, we do not need
        // to add a helper row
        if (!expandVerticalFound && !modelAccess.isFillVertical()) {
            int packingHelperRowIndex = gridlayout.getRows();
            gridlayout.setRows(packingHelperRowIndex + 1);
            gridlayout.setRowExpandRatio(packingHelperRowIndex, 1.0f);
        }

        if (!expandHorizontalFound && !modelAccess.isFillHorizontal()) {
            int packingHelperColumnIndex = gridlayout.getColumns();
            gridlayout.setColumns(packingHelperColumnIndex + 1);
            gridlayout.setColumnExpandRatio(packingHelperColumnIndex, 1.0f);
        }

    }

    /**
     * Is called to create the child component and apply layouting defaults to
     * it.
     * 
     * @param editpart
     * @param yStyle
     * @return
     */
    protected Cell addChild(IEmbeddableEditpart editpart, YGridLayoutCellStyle yStyle) {

        Component child = (Component) editpart.render(gridlayout);

        // calculate the spanning of the element
        // and adds the child to the grid layout
        //
        int col1 = -1;
        int row1 = -1;
        int col2 = -1;
        int row2 = -1;
        if (yStyle != null) {
            YSpanInfo ySpanInfo = yStyle.getSpanInfo();
            if (ySpanInfo != null) {
                col1 = ySpanInfo.getColumnFrom();
                row1 = ySpanInfo.getRowFrom();
                col2 = ySpanInfo.getColumnTo();
                row2 = ySpanInfo.getRowTo();
            }
        }

        // calculate and apply the alignment to be used
        //
        YAlignment yAlignment = yStyle != null && yStyle.getAlignment() != null ? yStyle.getAlignment() : null;
        if (yAlignment == null) {
            // use default
            yAlignment = YAlignment.TOP_LEFT;

            if (modelAccess.isFillVertical()) {
                // ensure that vertical alignment is FILL
                yAlignment = mapToVerticalFill(yAlignment);
            }
            if (modelAccess.isFillHorizontal()) {
                // ensure that horizontal alignment is FILL
                yAlignment = mapToHorizontalFill(yAlignment);
            }
        }

        // add the element to the grid layout
        //
        if (col1 >= 0 && row1 >= 0 && (col1 < col2 || row1 < row2)) {
            if (gridlayout.getRows() < row2 + 1) {
                gridlayout.setRows(row2 + 1);
            }
            gridlayout.addComponent(child, col1, row1, col2, row2);
        } else if (col1 < 0 || row1 < 0) {
            gridlayout.addComponent(child);
        } else {
            gridlayout.addComponent(child);
            LOGGER.warn("Invalid span: col1 {}, row1 {}, col2 {}, row2{}", new Object[] { col1, row1, col2, row2 });
        }
        applyAlignment(child, yAlignment);

        GridLayout.Area area = gridlayout.getComponentArea(child);

        return new Cell(child, yAlignment, area);
    }

    /**
     * Sets the alignment to the component.
     * 
     * @param child
     * @param yAlignment
     */
    protected void applyAlignment(Component child, YAlignment yAlignment) {

        if (yAlignment != null) {
            child.setWidth("-1%");
            child.setHeight("-1%");
            switch (yAlignment) {
            case BOTTOM_CENTER:
                gridlayout.setComponentAlignment(child, Alignment.BOTTOM_CENTER);
                break;
            case BOTTOM_FILL:
                gridlayout.setComponentAlignment(child, Alignment.BOTTOM_LEFT);
                child.setWidth("100%");
                break;
            case BOTTOM_LEFT:
                gridlayout.setComponentAlignment(child, Alignment.BOTTOM_LEFT);
                break;
            case BOTTOM_RIGHT:
                gridlayout.setComponentAlignment(child, Alignment.BOTTOM_RIGHT);
                break;
            case MIDDLE_CENTER:
                gridlayout.setComponentAlignment(child, Alignment.MIDDLE_CENTER);
                break;
            case MIDDLE_FILL:
                gridlayout.setComponentAlignment(child, Alignment.MIDDLE_LEFT);
                child.setWidth("100%");
                break;
            case MIDDLE_LEFT:
                gridlayout.setComponentAlignment(child, Alignment.MIDDLE_LEFT);
                break;
            case MIDDLE_RIGHT:
                gridlayout.setComponentAlignment(child, Alignment.MIDDLE_RIGHT);
                break;
            case TOP_CENTER:
                gridlayout.setComponentAlignment(child, Alignment.TOP_CENTER);
                break;
            case TOP_FILL:
                gridlayout.setComponentAlignment(child, Alignment.TOP_LEFT);
                child.setWidth("100%");
                break;
            case TOP_LEFT:
                gridlayout.setComponentAlignment(child, Alignment.TOP_LEFT);
                break;
            case TOP_RIGHT:
                gridlayout.setComponentAlignment(child, Alignment.TOP_RIGHT);
                break;
            case FILL_CENTER:
                gridlayout.setComponentAlignment(child, Alignment.TOP_CENTER);
                child.setHeight("100%");
                break;
            case FILL_FILL:
                gridlayout.setComponentAlignment(child, Alignment.TOP_LEFT);
                child.setWidth("100%");
                child.setHeight("100%");
                break;
            case FILL_LEFT:
                gridlayout.setComponentAlignment(child, Alignment.TOP_LEFT);
                child.setHeight("100%");
                break;
            case FILL_RIGHT:
                gridlayout.setComponentAlignment(child, Alignment.TOP_RIGHT);
                child.setHeight("100%");
                break;
            default:
                break;
            }
        }
    }

    /**
     * Maps the vertical part of the alignment to FILL.
     * 
     * @param yAlignment
     *            the alignment
     * @return alignment the mapped alignment
     */
    // BEGIN SUPRESS CATCH EXCEPTION
    protected YAlignment mapToVerticalFill(YAlignment yAlignment) {
        // END SUPRESS CATCH EXCEPTION
        if (yAlignment != null) {
            switch (yAlignment) {
            case BOTTOM_CENTER:
            case MIDDLE_CENTER:
            case TOP_CENTER:
                return YAlignment.FILL_CENTER;
            case BOTTOM_FILL:
            case MIDDLE_FILL:
            case TOP_FILL:
                return YAlignment.FILL_FILL;
            case BOTTOM_LEFT:
            case MIDDLE_LEFT:
            case TOP_LEFT:
                return YAlignment.FILL_LEFT;
            case BOTTOM_RIGHT:
            case MIDDLE_RIGHT:
            case TOP_RIGHT:
                return YAlignment.FILL_RIGHT;
            case FILL_FILL:
            case FILL_LEFT:
            case FILL_RIGHT:
            case FILL_CENTER:
                return YAlignment.FILL_FILL;
            default:
                break;
            }
        }
        return YAlignment.FILL_FILL;
    }

    /**
     * Maps the horizontal part of the alignment to FILL.
     * 
     * @param yAlignment
     *            the alignment
     * @return alignment the mapped alignment
     */
    // BEGIN SUPRESS CATCH EXCEPTION
    protected YAlignment mapToHorizontalFill(YAlignment yAlignment) {
        // END SUPRESS CATCH EXCEPTION
        if (yAlignment != null) {
            switch (yAlignment) {
            case BOTTOM_CENTER:
            case BOTTOM_FILL:
            case BOTTOM_LEFT:
            case BOTTOM_RIGHT:
                return YAlignment.BOTTOM_FILL;
            case MIDDLE_CENTER:
            case MIDDLE_FILL:
            case MIDDLE_LEFT:
            case MIDDLE_RIGHT:
                return YAlignment.MIDDLE_FILL;
            case TOP_CENTER:
            case TOP_FILL:
            case TOP_LEFT:
            case TOP_RIGHT:
                return YAlignment.TOP_FILL;
            case FILL_FILL:
            case FILL_LEFT:
            case FILL_RIGHT:
            case FILL_CENTER:
                return YAlignment.FILL_FILL;
            default:
                break;
            }
        }
        return YAlignment.FILL_FILL;
    }

    @Override
    public ComponentContainer doCreateWidget(Object parent) {
        if (componentBase == null) {
            componentBase = new CssLayout();
            componentBase.setSizeFull();
            componentBase.addStyleName(CSS_CLASS_CONTROL_BASE);
            if (modelAccess.isCssIdValid()) {
                componentBase.setId(modelAccess.getCssID());
            } else {
                componentBase.setId(getEditpart().getId());
            }

            associateWidget(componentBase, modelAccess.yLayout);

            gridlayout = new GridLayout(modelAccess.getColumns(), 1);
            gridlayout.setSpacing(false);
            gridlayout.setImmediate(true);
            componentBase.addComponent(gridlayout);

            associateWidget(gridlayout, modelAccess.yLayout);

            if (modelAccess.isMargin()) {
                gridlayout.addStyleName(IConstants.CSS_CLASS_MARGIN);
                gridlayout.setMargin(true);
            }

            if (modelAccess.isSpacing()) {
                gridlayout.setData(IConstants.CSS_CLASS_SPACING);
                gridlayout.setSpacing(true);
            }

            if (modelAccess.isCssClassValid()) {
                gridlayout.addStyleName(modelAccess.getCssClass());
            } else {
                gridlayout.addStyleName(CSS_CLASS_CONTROL);
            }

            renderChildren(false);
        }

        return componentBase;
    }

    @Override
    public ComponentContainer getWidget() {
        return componentBase;
    }

    @Override
    public boolean isRendered() {
        return componentBase != null;
    }

    @Override
    protected void internalDispose() {

        try {
            unrender();
        } finally {
            super.internalDispose();
        }

    }

    @Override
    public void doUnrender() {
        if (componentBase != null) {

            // unbind all active bindings
            unbind();

            ComponentContainer parent = ((ComponentContainer) componentBase.getParent());
            if (parent != null) {
                parent.removeComponent(componentBase);
            }

            // remove assocations
            unassociateWidget(componentBase);
            unassociateWidget(gridlayout);

            componentBase = null;
            gridlayout = null;

            // unrender the childs
            for (IEmbeddableEditpart child : getChildren()) {
                child.unrender();
            }
        }
    }

    @Override
    public void renderChildren(boolean force) {
        if (force) {
            unrenderChildren();
        }

        refreshUI();
    }

    /**
     * Will unrender all children.
     */
    protected void unrenderChildren() {
        for (IEmbeddableEditpart editpart : getChildren()) {
            if (editpart.isRendered()) {
                editpart.unrender();
            }
        }
    }

    /**
     * An internal helper class.
     */
    private static class ModelAccess {
        private final YGridLayout yLayout;

        public ModelAccess(YGridLayout yLayout) {
            super();
            this.yLayout = yLayout;
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.core.YCssAble#getCssClass()
         */
        public String getCssClass() {
            return yLayout.getCssClass();
        }

        /**
         * Returns true, if the css class is not null and not empty.
         * 
         * @return
         */
        public boolean isCssClassValid() {
            return getCssClass() != null && !getCssClass().equals("");
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.extension.YGridLayout#isSpacing()
         */
        public boolean isSpacing() {
            return yLayout.isSpacing();
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.core.YCssAble#getCssID()
         */
        public String getCssID() {
            return yLayout.getCssID();
        }

        /**
         * Returns true, if the css id is not null and not empty.
         * 
         * @return
         */
        public boolean isCssIdValid() {
            return getCssID() != null && !getCssID().equals("");
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.extension.YGridLayout#isMargin()
         */
        public boolean isMargin() {
            return yLayout.isMargin();
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.extension.YGridLayout#getColumns()
         */
        public int getColumns() {
            int columns = yLayout.getColumns();
            return columns <= 0 ? 2 : columns;
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.extension.YGridLayout#getCellStyles()
         */
        public EList<YGridLayoutCellStyle> getCellStyles() {
            return yLayout.getCellStyles();
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.extension.YGridLayout#isFillHorizontal()
         */
        public boolean isFillHorizontal() {
            return yLayout.isFillHorizontal();
        }

        /**
         * @return
         * @see org.eclipse.emf.ecp.ecview.ui.core.model.extension.YGridLayout#isFillVertical()
         */
        public boolean isFillVertical() {
            return yLayout.isFillVertical();
        }

    }

    private static class Row {
        private int rowindex;
        private List<Cell> cells;
        private boolean shouldExpandVertical;

        private Row(int rowindex, int columns) {
            this.rowindex = rowindex;
            cells = new ArrayList<Cell>(columns);
        }

        public void addCell(int column, Cell cell) {
            cells.add(column, cell);

            YAlignment alignment = cell.getAlignment();
            // if not already sure, that it should expand
            // try to find out
            if (!shouldExpandVertical) {
                // If the cell should FILL-vertical, then we test if the cell is
                // spanned.
                // --> If not spanned, then "shouldExpandVertical" is true.
                // --> Otherwise we test, if the cell is the most bottom cell.
                // ----> If not most bottom, then no span.
                // ----> Otherwise "shouldExpandVertical" is true.
                switch (alignment) {
                case FILL_LEFT:
                case FILL_CENTER:
                case FILL_RIGHT:
                case FILL_FILL:
                    if (!cell.isSpanned()) {
                        // if the cell is not spanned, then
                        // "shouldExpandHorizontal" is true
                        shouldExpandVertical = true;
                    } else {
                        if (cell.getArea().getRow2() == rowindex) {
                            // if the cell is the most right one, then
                            // "shouldExpandHorizontal" is true
                            shouldExpandVertical = true;
                        }
                    }
                    break;
                default:
                    // nothing to do
                    break;
                }
            }
        }

        /**
         * @return the rowindex
         */
        protected int getRowindex() {
            return rowindex;
        }

        /**
         * @return the shouldExpandVertical
         */
        protected boolean isShouldExpandVertical() {
            return shouldExpandVertical;
        }

    }

    private static class Column {
        private final int columnindex;
        private List<Cell> cells;
        private boolean shouldExpandHorizontal;

        private Column(int columnindex, int rows) {
            this.columnindex = columnindex;
            cells = new ArrayList<Cell>(rows);
        }

        public void addCell(int row, Cell cell) {
            try {
                cells.add(row, cell);
            } catch (Exception e) {
                System.out.println(e);
            }
            YAlignment alignment = cell.getAlignment();
            // if not already sure, that it should expand
            // try to find out
            if (!shouldExpandHorizontal) {
                // If the cell should FILL-horizontal, then we test if the cell
                // is spanned.
                // --> If not spanned, then "shouldExpandHorizontal" is true.
                // --> Otherwise we test, if the cell is the most right cell.
                // ----> If not most right, then no span.
                // ----> Otherwise "shouldExpandHorizontal" is true.
                switch (alignment) {
                case BOTTOM_FILL:
                case MIDDLE_FILL:
                case TOP_FILL:
                case FILL_FILL:
                    if (!cell.isSpanned()) {
                        // if the cell is not spanned, then
                        // "shouldExpandHorizontal" is true
                        shouldExpandHorizontal = true;
                    } else {
                        if (cell.getArea().getColumn2() == cells.size() - 1) {
                            // if the cell is the most right one, then
                            // "shouldExpandHorizontal" is true
                            shouldExpandHorizontal = true;
                        }
                    }
                    break;
                default:
                    // nothing to do
                    break;
                }
            }
        }

        /**
         * @return the columnindex
         */
        protected int getColumnindex() {
            return columnindex;
        }

        /**
         * @return the shouldExpandHorizontal
         */
        protected boolean isShouldExpandHorizontal() {
            return shouldExpandHorizontal;
        }

    }

    public static class Cell {
        private final Component component;
        private final YAlignment alignment;
        private final Area area;

        public Cell(Component component, YAlignment alignment, GridLayout.Area area) {
            super();
            this.component = component;
            this.alignment = alignment;
            this.area = area;
        }

        /**
         * @return the component
         */
        protected Component getComponent() {
            return component;
        }

        /**
         * @return the alignment
         */
        protected YAlignment getAlignment() {
            return alignment;
        }

        /**
         * @return the area
         */
        protected Area getArea() {
            return area;
        }

        /**
         * Returns true, if the cell is spanned.
         * 
         * @return
         */
        public boolean isSpanned() {
            return area.getRow1() != area.getRow2() || area.getColumn1() != area.getColumn2();
        }

    }
}