asquare.gwt.tk.client.ui.ExposedCellPanel.java Source code

Java tutorial

Introduction

Here is the source code for asquare.gwt.tk.client.ui.ExposedCellPanel.java

Source

/*
 * Copyright 2006 Mat Gessel <mat.gessel@gmail.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * 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 asquare.gwt.tk.client.ui;

import java.util.ArrayList;
import java.util.List;

import asquare.gwt.tk.client.util.GwtUtil;
import asquare.gwt.tk.client.util.TableUtil;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.Widget;

/**
 * A table-based panel which {@link #getCellElement(int) exposes} the TD element
 * and supports:
 * <ul>
 * <li>setting styles on the TD</li>
 * <li>multiple widgets per cell</li>
 * <li>empty cells</li>
 * </ul>
 */
public abstract class ExposedCellPanel extends CellPanel implements HasAlignment {
    /**
     * Maps the index of each cell to an ordered list of child widgets. 
     * Mapping may be null if the cell contains no widgets. <br/>
     */
    private final List<List<Widget>> m_cellMap = new ArrayList<List<Widget>>();

    private HorizontalAlignmentConstant m_horizontalAlignment = ALIGN_LEFT;
    private VerticalAlignmentConstant m_verticalAlignment = ALIGN_TOP;

    /**
     * Creates an empty panel with no cells. 
     */
    ExposedCellPanel() {
        TableUtil.setTableCellPadding(getTable(), 0);
        TableUtil.setTableCellSpacing(getTable(), 0);
    }

    /**
     * Gets the number of cells in this panel. 
     */
    public int getCellCount() {
        return m_cellMap.size();
    }

    /**
     * Adds a new cell to the panel. 
     */
    public void addCell() {
        insertCell(m_cellMap.size());
    }

    /**
     * Creates a new cell and inserts it at the specified index. Existing cells
     * with an index >= <code>cellIndex</code> will be shifted over by 1.
     * 
     * @param cellIndex the index where the cell will be inserted
     * @throws IndexOutOfBoundsException if <code>cellIndex</code> is less
     *             than 0 or greater than the number of cells
     */
    public void insertCell(int cellIndex) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, true);

        m_cellMap.add(cellIndex, null);
        insertCellStructure(cellIndex);
        if (m_horizontalAlignment != null) {
            setCellHorizontalAlignment(cellIndex, m_horizontalAlignment);
        }
        if (m_verticalAlignment != null) {
            setCellVerticalAlignment(cellIndex, m_verticalAlignment);
        }
    }

    /**
     * A <em>template method</em> which creates a td and inserts it into the
     * underlying table. If a cell exists at <code>cellIndex</code> it will be
     * shifted up by 1. Implementors may assume <code>cellIndex</code> is
     * greater than 0 and less than or equal to the number of cells.
     * 
     * @param cellIndex the index at which the td will be inserted.
     */
    protected abstract void insertCellStructure(int cellIndex);

    /**
     * Removes a cell from the panel, including any child widets.
     * 
     * @param cellIndex the index of the cell
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     */
    public void removeCell(int cellIndex) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        clearCell(cellIndex);
        removeCellStructure(cellIndex);
        m_cellMap.remove(cellIndex);
    }

    /**
     * Removes all widgets and child elements from the cell
     * 
     * @param cellIndex the index of the cell
     * @throws IndexOutOfBoundsException if cellIndex does not specify a valid
     *             cell
     */
    public void clearCell(int cellIndex) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        List<Widget> cellWidgets = m_cellMap.get(cellIndex);
        if (cellWidgets != null) {
            while (!cellWidgets.isEmpty()) {
                removeWidget(cellWidgets.get(cellWidgets.size() - 1), cellIndex);
            }
        }
        DOM.setInnerHTML(getCellElement(cellIndex), "");
        m_cellMap.set(cellIndex, null);
    }

    /**
     * A <em>template method</em> which removes a td from the underlying
     * table. Cells with indexes greater than <code>cellIndex</code> will be
     * shifted down by 1. Implementors may assume <code>cellIndex</code> is
     * greater than 0 and less than the number of cells.
     * 
     * @param cellIndex the index at which the td will be inserted.
     */
    protected abstract void removeCellStructure(int cellIndex);

    /**
     * Removes all cells and child widgets from the panel. 
     */
    @Override
    public void clear() {
        while (!m_cellMap.isEmpty()) {
            removeCell(m_cellMap.size() - 1);
        }
    }

    /**
     * Gets the number of child widgets added to the panel
     * 
     * @return the number of child widgets
     */
    @Override
    public int getWidgetCount() {
        return getChildren().size();
    }

    /**
     * Gets a at the specified index in the specified cell. <code>wIndex</code>
     * does not include non-widget child elements.
     * 
     * @param cellIndex the index of the cell, starting with 0
     * @param wIndex the index of the widget in the specified cell, starting at
     *            0
     * @return the widget
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     * @throws IndexOutOfBoundsException if the widget specified by
     *             <code>wIndex</code> does not exist
     */
    public Widget getWidgetAt(int cellIndex, int wIndex) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        List<Widget> childWidgets = m_cellMap.get(cellIndex);

        if (childWidgets == null)
            throw new IndexOutOfBoundsException(Integer.toString(wIndex));

        GwtUtil.rangeCheck(childWidgets, wIndex, false);

        return childWidgets.get(wIndex);
    }

    /**
     * Get the index of the cell which contains the specified widget.
     * 
     * @param w a child widget
     * @return the index or <code>-1</code> if the widget is not a child of
     *         this panel
     */
    public int getCellIndexOf(Widget w) {
        int result = -1;
        int cell = 0, size = m_cellMap.size();
        while (cell < size) {
            List<Widget> cellWidgets = m_cellMap.get(cell);
            if (cellWidgets != null && cellWidgets.contains(w)) {
                result = cell;
                break;
            }
            cell++;
        }
        return result;
    }

    /**
     * Creates a new cell and appends to it the specified widget. 
     * 
     * @param w a widget
     */
    @Override
    public void add(Widget w) {
        addWidget(w, true);
    }

    /**
     * Adds a widget to the panel, optionally creating a new cell. 
     * 
     * @param w a widget
     * @param newCell <code>true</code> to create a new cell,
     *            <code>false</code> to append to the last cell
     * @throws IndexOutOfBoundsException if <code>newCell</code> is
     *             <code>false</code> and no cells exist
     */
    public void addWidget(Widget w, boolean newCell) {
        /*
         * if w is a child of this panel and the only widget in a cell the cell
         * will be removed
         */
        w.removeFromParent();

        if (newCell) {
            insertCell(m_cellMap.size());
        }
        addWidgetTo(w, m_cellMap.size() - 1);
    }

    /**
     * Adds a widget to the specified cell. <code>w</code> will be appended
     * after any other widgets in the cell.
     * 
     * @param w a widget
     * @param cellIndex the index of the cell
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     */
    public void addWidgetTo(Widget w, int cellIndex) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        List<Widget> cellWidgets = m_cellMap.get(cellIndex);
        int wIndex = (cellWidgets != null) ? cellWidgets.size() : 0;
        insertWidgetAt(w, cellIndex, wIndex);
    }

    /**
     * Inserts a new cell at the specified index and appends the widget to the
     * cell.
     * 
     * @param w a widget
     * @param cellIndex the index of the cell
     * @throws IndexOutOfBoundsException if <code>cellIndex</code> is less
     *             than 0 or greater than the number of cells
     */
    public void insert(Widget w, int cellIndex) {
        /*
         * if w is a child of this panel and the only widget in a cell the cell
         * will be removed
         */
        w.removeFromParent();

        insertCell(cellIndex);
        insertWidgetAt(w, cellIndex, 0);
    }

    /**
     * Inserts a widget into a an existing cell. Any widgets with indexes
     * greater than or equal to <code>wIndex</code> will be shifted over.
     * 
     * @param w a widget
     * @param cellIndex the index of the cell
     * @param wIndex the index of the widget before which <code>w</code> will
     *            be inserted
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     * @throws IndexOutOfBoundsException if <code>wIndex</code> is less than 0
     *             or greater than the number of widgets in the specified cell
     */
    public void insertWidgetAt(Widget w, int cellIndex, int wIndex) {
        /*
         * The cell will be removed if w is a child of this panel and w is the
         * only widget in the cell
         */
        w.removeFromParent();

        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        List<Widget> cellWidgets = m_cellMap.get(cellIndex);

        if (cellWidgets == null) {
            if (wIndex != 0)
                throw new IndexOutOfBoundsException(Integer.toString(wIndex));

            cellWidgets = new ArrayList<Widget>();
            m_cellMap.set(cellIndex, cellWidgets); // pre: cellIndex has been inserted
        }

        GwtUtil.rangeCheck(cellWidgets, wIndex, true);

        cellWidgets.add(wIndex, w);

        Element td = getCellElement(cellIndex);
        insert(w, td, wIndex, true);
    }

    /**
     * Removes a widget from the panel. 
     * Automatically removes the cell if it becomes empty. 
     * 
     * @param w a child widget
     * @return false if <code>w</code> is not a child of this panel
     */
    @Override
    public boolean remove(Widget w) {
        return remove(w, true);
    }

    /**
     * Removes a widget from the panel, optionally removing the cell if it
     * becomes empty.
     * 
     * @param w a child widget
     * @param removeEmptyCell <code>true</code> to remove the cell if it
     *            becomes empty
     * @return false if <code>w</code> is not a child of this panel
     */
    public boolean remove(Widget w, boolean removeEmptyCell) {
        if (!getChildren().contains(w))
            return false;

        int cellIndex = getCellIndexOf(w);
        assert cellIndex >= 0;

        removeWidget(w, cellIndex);
        List<Widget> childWidgets = m_cellMap.get(cellIndex);
        if (removeEmptyCell && childWidgets.size() == 0) {
            removeCell(cellIndex);
        }
        return true;
    }

    /*
     * Removes the widget from the cellMap & DOM tree and clears out its
     * listener
     */
    private void removeWidget(Widget w, int cellIndex) {
        List<Widget> childWidgets = m_cellMap.get(cellIndex);

        super.remove(w);
        boolean present = (childWidgets).remove(w);
        assert present;
    }

    /**
     * Gets the table <code>td</code> element corresponding to last cell.
     * 
     * @return the <code>td</code> element of the last cell
     * @throws IllegalStateException if no cells exist
     */
    public Element getCellElement() {
        if (getCellCount() == 0)
            throw new IllegalStateException();

        return getCellElement(getCellCount() - 1);
    }

    /**
     * Gets the table <code>td</code> element corresponding to the specified
     * cell.
     * 
     * @param cellIndex the index of the cell
     * @return the <code>td</code> element of the specified cell
     * @throws IndexOutOfBoundsException if <code>cellIndex</code> does not
     *             specify an existing cell
     */
    public abstract Element getCellElement(int cellIndex);

    /**
     * Gets the style name(s) for the cell specified by
     * <code>cellIndex</code>.
     * 
     * @return the CSS class name(s) (space delimited)
     * @param cellIndex the index of the cell
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     */
    public String getCellStyleName(int cellIndex) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        // TODO: remove null check in 1.5, http://code.google.com/p/google-web-toolkit/issues/detail?id=1770
        String result = DOM.getElementProperty(getCellElement(cellIndex), "className");
        return result != null ? result : "";
    }

    /**
     * Adds a style name to the last cell. 
     * 
     * @param styleName a CSS class name
     * @throws IllegalStateException if no cells exist
     * @throws IllegalArgumentException if <code>styleName</code> is <code>&quot;&quot;</code>
     */
    public void addCellStyleName(String styleName) {
        if (getCellCount() == 0)
            throw new IllegalStateException();

        addCellStyleName(getCellCount() - 1, styleName);
    }

    /**
     * Adds a style name to the <code>class</code> attribute of the cell
     * specified by the <code>cellIndex</code>.
     * 
     * @param cellIndex the index of a cell
     * @param styleName a CSS class name
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     * @throws IllegalArgumentException if <code>styleName</code> is <code>&quot;&quot;</code>
     */
    public void addCellStyleName(int cellIndex, String styleName) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        setStyleName(getCellElement(cellIndex), styleName, true);
    }

    /**
     * Sets the style name for the last cell. Other style names will be
     * overwritten.
     * 
     * @param styleName a CSS class name
     * @throws IllegalStateException if no cells exist
     */
    public void setCellStyleName(String styleName) {
        if (m_cellMap.size() == 0)
            throw new IllegalStateException();

        setCellStyleName(getCellCount() - 1, styleName);
    }

    /**
     * Sets the style name for the cell specified by
     * <code>cellIndex</code>. Other style names will be
     * overwritten.
     * 
     * @param cellIndex the index of the cell
     * @param styleName a CSS class name
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     */
    public void setCellStyleName(int cellIndex, String styleName) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        DOM.setElementProperty(getCellElement(cellIndex), "className", styleName);
    }

    /**
     * Removes the specified style name from the last cell. Does nothing if
     * <code>styleName</code> is not present.
     * 
     * @param styleName a CSS class name
     * @throws IllegalStateException if no cells exist
     * @throws IllegalArgumentException if <code>styleName</code> is
     *             <code>&quot;&quot;</code>
     */
    public void removeCellStyleName(String styleName) {
        if (getCellCount() == 0)
            throw new IllegalStateException();

        removeCellStyleName(getCellCount() - 1, styleName);
    }

    /**
     * Adds a style name to the cell specified by
     * <code><cellIndex/code>. Does nothing if
     * <code>styleName</code> is not present.
     * 
     * @param cellIndex the index of the cell
     * @param styleName a CSS class name
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     * @throws IllegalArgumentException if <code>styleName</code> is <code>&quot;&quot;</code>
     */
    public void removeCellStyleName(int cellIndex, String styleName) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        setStyleName(getCellElement(cellIndex), styleName, false);
    }

    /**
     * Sets the width of the last cell. 
     * 
     * @param width a CSS measurement
     */
    public void setCellWidth(String width) {
        if (m_cellMap.size() == 0)
            throw new IllegalStateException();

        setCellWidth(getCellCount() - 1, width);
    }

    /**
     * Sets the width of the specified cell. 
     * 
     * @param cellIndex the index of a cell
     * @param width a CSS measurement
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     */
    public void setCellWidth(int cellIndex, String width) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        TableUtil.setTdWidth(getCellElement(cellIndex), width);
    }

    /**
     * Sets the height of the last cell. 
     * 
     * @param height a CSS measurement
     */
    public void setCellHeight(String height) {
        if (m_cellMap.size() == 0)
            throw new IllegalStateException();

        setCellHeight(getCellCount() - 1, height);
    }

    /**
     * Sets the height of the specified cell. 
     * 
     * @param cellIndex the index of a cell
     * @param height a CSS measurement
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     */
    public void setCellHeight(int cellIndex, String height) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        TableUtil.setTdHeight(getCellElement(cellIndex), height);
    }

    /**
     * Sets the horizontal alignment of the last cell. 
     * 
     * @param hAlign a constant representing left, center or right alignment
     * @see HasAlignment
     */
    public void setCellHorizontalAlignment(HorizontalAlignmentConstant hAlign) {
        if (m_cellMap.size() == 0)
            throw new IllegalStateException();

        setCellHorizontalAlignment(getCellCount() - 1, hAlign);
    }

    /**
     * Sets the horizontal alignment of the specified cell. 
     * 
     * @param cellIndex the index of a cell
     * @param hAlign a constant representing left, center or right alignment
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     * @see HasAlignment
     */
    public void setCellHorizontalAlignment(int cellIndex, HorizontalAlignmentConstant hAlign) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        TableUtil.setTdHorizontalAlignment(getCellElement(cellIndex), hAlign);
    }

    /**
     * Sets the vertical alignment of the last cell. 
     * 
     * @param vAlign a constant representing top, middle or bottom alignment
     * @see HasAlignment
     */
    public void setCellVerticalAlignment(VerticalAlignmentConstant vAlign) {
        if (m_cellMap.size() == 0)
            throw new IllegalStateException();

        setCellVerticalAlignment(getCellCount() - 1, vAlign);
    }

    /**
     * Sets the vertical alignment of the specified cell. 
     * 
     * @param cellIndex the index of a cell
     * @param vAlign a constant representing top, middle or bottom alignment
     * @throws IndexOutOfBoundsException if the cell specified by
     *             <code>cellIndex</code> does not exist
     * @see HasAlignment
     */
    public void setCellVerticalAlignment(int cellIndex, VerticalAlignmentConstant vAlign) {
        GwtUtil.rangeCheck(m_cellMap, cellIndex, false);

        TableUtil.setTdVerticalAlignment(getCellElement(cellIndex), vAlign);
    }

    /**
     * Gets the default horizontal alignment for newly created cells.
     * 
     * @return an alignment constant or null
     * @see HasAlignment
     */
    public HorizontalAlignmentConstant getHorizontalAlignment() {
        return m_horizontalAlignment;
    }

    /**
     * Sets the default horizontal alignment for newly created cells.
     * 
     * @param align an alignment constant or null
     * @see HasAlignment
     */
    public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
        m_horizontalAlignment = align;
    }

    /**
     * Gets the default vertical alignment for newly created cells.
     * 
     * @return an alignment constant or null
     * @see HasAlignment
     */
    public VerticalAlignmentConstant getVerticalAlignment() {
        return m_verticalAlignment;
    }

    /**
     * Sets the default vertical alignment for newly created cells.
     * 
     * @param align an alignment constant or null
     * @see HasAlignment
     */
    public void setVerticalAlignment(VerticalAlignmentConstant align) {
        m_verticalAlignment = align;
    }
}