com.google.gdt.eclipse.designer.uibinder.model.widgets.GridInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gdt.eclipse.designer.uibinder.model.widgets.GridInfo.java

Source

/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.google.gdt.eclipse.designer.uibinder.model.widgets;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.gdt.eclipse.designer.GwtToolkitDescription;
import com.google.gdt.eclipse.designer.uibinder.Activator;

import org.eclipse.wb.core.model.ObjectInfo;
import org.eclipse.wb.core.model.broadcast.ObjectEventListener;
import org.eclipse.wb.core.model.broadcast.ObjectInfoDelete;
import org.eclipse.wb.core.model.broadcast.ObjectInfoPresentationDecorateText;
import org.eclipse.wb.core.model.broadcast.ObjectInfoTreeComplete;
import org.eclipse.wb.draw2d.geometry.Rectangle;
import org.eclipse.wb.internal.core.model.util.ObjectInfoAction;
import org.eclipse.wb.internal.core.utils.GenericsUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.xml.DocumentElement;
import org.eclipse.wb.internal.core.xml.editor.DesignContextMenuProvider;
import org.eclipse.wb.internal.core.xml.model.AbstractComponentInfo;
import org.eclipse.wb.internal.core.xml.model.EditorContext;
import org.eclipse.wb.internal.core.xml.model.XmlObjectInfo;
import org.eclipse.wb.internal.core.xml.model.association.Associations;
import org.eclipse.wb.internal.core.xml.model.broadcast.XmlObjectMove;
import org.eclipse.wb.internal.core.xml.model.creation.CreationSupport;
import org.eclipse.wb.internal.core.xml.model.creation.TagCreationSupport;
import org.eclipse.wb.internal.core.xml.model.description.ComponentDescription;
import org.eclipse.wb.internal.core.xml.model.description.ComponentDescriptionHelper;
import org.eclipse.wb.internal.core.xml.model.utils.XmlObjectUtils;

import org.eclipse.jface.action.IMenuManager;
import org.eclipse.swt.graphics.Image;

import org.apache.commons.lang.StringUtils;

import java.util.List;
import java.util.Map;

/**
 * Model for <code>com.google.gwt.user.client.ui.Grid</code> in GWT UiBinder.
 * 
 * @author scheglov_ke
 * @coverage GWT.UiBinder.model
 */
public final class GridInfo extends PanelInfo {
    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public GridInfo(EditorContext context, ComponentDescription description, CreationSupport creationSupport)
            throws Exception {
        super(context, description, creationSupport);
        parseRowsCells();
        decorateCellText();
        removeEmptyCustomCell();
        contributeCellContextMenu();
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Broadcasts
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Parses "row", "cell" and "customCell" elements into appropriate models.
     */
    private void parseRowsCells() {
        addBroadcastListener(new ObjectInfoTreeComplete() {
            public void invoke() throws Exception {
                Map<DocumentElement, CustomCell> customCells = Maps.newHashMap();
                DocumentElement gridElement = getElement();
                for (DocumentElement rowElement : gridElement.getChildren()) {
                    if (rowElement.getTagLocal().equals("row")) {
                        Row row = new Row(rowElement);
                        addChild(row);
                        for (DocumentElement cellElement : rowElement.getChildren()) {
                            if (cellElement.getTagLocal().equals("cell")) {
                                HtmlCell cell = new HtmlCell(cellElement);
                                row.addChild(cell);
                            }
                            if (cellElement.getTagLocal().equals("customCell")) {
                                CustomCell cell = new CustomCell(cellElement);
                                row.addChild(cell);
                                customCells.put(cellElement, cell);
                            }
                        }
                    }
                }
                // re-bind Widget children to "customCell" models
                for (WidgetInfo widget : getChildrenWidgets()) {
                    DocumentElement cellElement = widget.getElement().getParent();
                    CustomCell cell = customCells.get(cellElement);
                    removeChild(widget);
                    cell.addChild(widget);
                }
            }
        });
    }

    /**
     * Decorates text of {@link Cell} with short excerpt of its content.
     */
    private void decorateCellText() {
        addBroadcastListener(new ObjectInfoPresentationDecorateText() {
            public void invoke(ObjectInfo object, String[] text) throws Exception {
                if (object instanceof HtmlCell && isParentOf(object)) {
                    DocumentElement element = ((HtmlCell) object).getElement();
                    int beginIndex = element.getOpenTagOffset() + element.getOpenTagLength();
                    int endIndex = element.getCloseTagOffset();
                    String contentText = getContext().getContent().substring(beginIndex, endIndex);
                    contentText = contentText.trim();
                    contentText = StringUtils.substring(contentText, 0, 15);
                    text[0] += " " + contentText;
                }
            }
        });
    }

    /**
     * {@link CustomCell} should always have a {@link WidgetInfo} child, so when it looses it, we
     * should remove {@link CustomCell} too.
     */
    private void removeEmptyCustomCell() {
        addBroadcastListener(new XmlObjectMove() {
            @Override
            public void after(XmlObjectInfo child, ObjectInfo oldParent, ObjectInfo newParent) throws Exception {
                if (oldParent instanceof CustomCell && isParentOf(oldParent)) {
                    oldParent.delete();
                }
            }
        });
        addBroadcastListener(new ObjectInfoDelete() {
            @Override
            public void after(ObjectInfo parent, ObjectInfo child) throws Exception {
                if (parent instanceof CustomCell && isParentOf(parent) && !parent.isDeleting()) {
                    parent.delete();
                }
            }
        });
    }

    private void contributeCellContextMenu() {
        addBroadcastListener(new ObjectEventListener() {
            @Override
            public void addContextMenu(List<? extends ObjectInfo> objects, ObjectInfo object, IMenuManager manager)
                    throws Exception {
                if (object instanceof Cell && isParentOf(object)) {
                    final Cell cell = (Cell) object;
                    final Row row = (Row) cell.getParent();
                    addAction(manager, "Insert cell", "insertCell", new RunnableEx() {
                        public void run() throws Exception {
                            createCell(row, cell);
                        }
                    });
                    addAction(manager, "Append cell", "appendCell", new RunnableEx() {
                        public void run() throws Exception {
                            Cell nextCell = GenericsUtils.getNextOrNull(row.getCells(), cell);
                            createCell(row, nextCell);
                        }
                    });
                }
            }

            private void addAction(IMenuManager manager, String text, String iconName, final RunnableEx runnable) {
                Image icon = Activator.getImage("info/Grid/" + iconName + ".png");
                manager.appendToGroup(DesignContextMenuProvider.GROUP_TOP,
                        new ObjectInfoAction(GridInfo.this, text, icon) {
                            @Override
                            protected void runEx() throws Exception {
                                runnable.run();
                            }
                        });
            }

            private void createCell(Row row, Cell nextCell) throws Exception {
                HtmlCell newCell = new HtmlCell();
                row.command_CREATE(newCell, nextCell);
                newCell.getElement().setText("New cell", false);
                row.getBroadcastObject().select(ImmutableList.of(newCell));
            }
        });
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Access
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the {@link Row} children.
     */
    public List<Row> getRows() {
        return getChildren(Row.class);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Refresh
    //
    ////////////////////////////////////////////////////////////////////////////
    @Override
    protected void refresh_fetch() throws Exception {
        setElementsForParts();
        super.refresh_fetch();
    }

    /**
     * Sets DOM elements for {@link GridElement} models.
     */
    private void setElementsForParts() throws Exception {
        Object bodyElement = ReflectionUtils.invokeMethod(getObject(), "getBodyElement()");
        List<Row> rows = getRows();
        Object[] rowElements = getDOM().getChildren(bodyElement);
        for (int i = 0; i < rowElements.length; i++) {
            Object rowElement = rowElements[i];
            if (i < rows.size()) {
                Row row = rows.get(i);
                row.setElement(rowElement);
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Commands
    //
    ////////////////////////////////////////////////////////////////////////////
    public void command_MOVE(Row row, Row nextRow) throws Exception {
        XmlObjectUtils.move(row, Associations.direct(), this, nextRow);
    }

    public void command_ADD(Cell cell, Row nextRow) throws Exception {
        Row newRow = command_addNewRow(nextRow);
        newRow.command_MOVE(cell, null);
    }

    public void command_CREATE(WidgetInfo newWidget, Row nextRow) throws Exception {
        Row newRow = command_addNewRow(nextRow);
        newRow.command_CREATE(newWidget, null);
    }

    public void command_ADD(WidgetInfo widget, Row nextRow) throws Exception {
        Row newRow = command_addNewRow(nextRow);
        newRow.command_ADD(widget, null);
    }

    private Row command_addNewRow(Row nextRow) throws Exception {
        Row newRow = new Row();
        XmlObjectUtils.add(newRow, Associations.direct(), this, nextRow);
        return newRow;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // GridElement
    //
    ////////////////////////////////////////////////////////////////////////////
    private final Map<String, ComponentDescription> m_gridElementDescriptions = Maps.newTreeMap();

    private ComponentDescription getElementDescription(String name) throws Exception {
        ComponentDescription description = m_gridElementDescriptions.get(name);
        if (description == null) {
            description = new ComponentDescription(Object.class);
            description.setToolkit(GwtToolkitDescription.INSTANCE);
            description.setIcon(ComponentDescriptionHelper.getIcon(getContext(),
                    getDescription().getComponentClass(), "_" + name));
            m_gridElementDescriptions.put(name, description);
        }
        return description;
    }

    public class GridElement extends AbstractComponentInfo {
        private Rectangle m_absoluteBounds;

        ////////////////////////////////////////////////////////////////////////////
        //
        // Constructor
        //
        ////////////////////////////////////////////////////////////////////////////
        public GridElement(ComponentDescription description, DocumentElement element) throws Exception {
            super(GridInfo.this.getContext(), description, new TagCreationSupport(element));
        }

        public GridElement(ComponentDescription description, String tag) throws Exception {
            super(GridInfo.this.getContext(), description,
                    new TagCreationSupport(GridInfo.this.getElement().getTagNS() + tag));
        }

        @Override
        public Rectangle getAbsoluteBounds() {
            return m_absoluteBounds;
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Refresh
        //
        ////////////////////////////////////////////////////////////////////////////
        @Override
        protected void refresh_fetch() throws Exception {
            fetchBounds();
            super.refresh_fetch();
        }

        private void fetchBounds() {
            Object element = getObject();
            // absolute bounds
            m_absoluteBounds = getState().getAbsoluteBounds(element);
            // model/parent bounds
            Rectangle bounds = m_absoluteBounds.getCopy();
            AbstractComponentInfo parent = (AbstractComponentInfo) getParent();
            bounds.translate(parent.getAbsoluteBounds().getLocation().getNegated());
            bounds.translate(parent.getClientAreaInsets().getNegated());
            setModelBounds(bounds);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Row
    //
    ////////////////////////////////////////////////////////////////////////////
    public final class Row extends GridElement {
        ////////////////////////////////////////////////////////////////////////////
        //
        // Constructor
        //
        ////////////////////////////////////////////////////////////////////////////
        public Row() throws Exception {
            super(getElementDescription("row"), "row");
        }

        public Row(DocumentElement element) throws Exception {
            super(getElementDescription("row"), element);
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Access
        //
        ////////////////////////////////////////////////////////////////////////////
        /**
         * @return the {@link Cell} children.
         */
        public List<Cell> getCells() {
            return getChildren(Cell.class);
        }

        /**
         * Sets DOM element for this {@link Row} and its {@link Cell} children.
         */
        public void setElement(Object rowElement) throws Exception {
            setObject(rowElement);
            List<Cell> cells = getCells();
            Object[] cellElements = getDOM().getChildren(rowElement);
            for (int i = 0; i < cellElements.length; i++) {
                Object cellElement = cellElements[i];
                if (i < cells.size()) {
                    Cell cell = cells.get(i);
                    cell.setObject(cellElement);
                }
            }
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Commands
        //
        ////////////////////////////////////////////////////////////////////////////
        public void command_CREATE(Cell cell, Cell nextCell) throws Exception {
            XmlObjectUtils.add(cell, Associations.direct(), this, nextCell);
        }

        public void command_MOVE(Cell cell, Cell nextCell) throws Exception {
            XmlObjectUtils.move(cell, Associations.direct(), this, nextCell);
        }

        public void command_CREATE(WidgetInfo newWidget, Cell nextCell) throws Exception {
            CustomCell newCell = command_addNewCustomCell(nextCell);
            newCell.command_CREATE(newWidget);
        }

        public void command_ADD(WidgetInfo widget, Cell nextCell) throws Exception {
            CustomCell newCell = command_addNewCustomCell(nextCell);
            newCell.command_ADD(widget);
        }

        private CustomCell command_addNewCustomCell(Cell nextCell) throws Exception {
            CustomCell newCell = new CustomCell();
            command_CREATE(newCell, nextCell);
            return newCell;
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Cell
    //
    ////////////////////////////////////////////////////////////////////////////
    public class Cell extends GridElement {
        public Cell(ComponentDescription description, DocumentElement element) throws Exception {
            super(description, element);
        }

        public Cell(ComponentDescription description, String tag) throws Exception {
            super(description, tag);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // HtmlCell
    //
    ////////////////////////////////////////////////////////////////////////////
    public final class HtmlCell extends Cell {
        public HtmlCell(DocumentElement element) throws Exception {
            super(getElementDescription("cell"), element);
        }

        public HtmlCell() throws Exception {
            super(getElementDescription("cell"), "cell");
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // CustomCell
    //
    ////////////////////////////////////////////////////////////////////////////
    public final class CustomCell extends Cell {
        ////////////////////////////////////////////////////////////////////////////
        //
        // Constructor
        //
        ////////////////////////////////////////////////////////////////////////////
        public CustomCell(DocumentElement element) throws Exception {
            super(getElementDescription("customCell"), element);
        }

        public CustomCell() throws Exception {
            super(getElementDescription("customCell"), "customCell");
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Command
        //
        ////////////////////////////////////////////////////////////////////////////
        public void command_CREATE(WidgetInfo widget) throws Exception {
            XmlObjectUtils.add(widget, Associations.direct(), this, null);
        }

        public void command_ADD(WidgetInfo widget) throws Exception {
            XmlObjectUtils.move(widget, Associations.direct(), this, null);
        }
    }
}