907 lines · 31.0 KB · Java
RawDownload
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.awt.*;import java.util.BitSet;import java.util.Vector;import javax.swing.SizeRequirements;import javax.swing.event.DocumentEvent; import javax.swing.text.html.HTML; /** * <p> * Implements View interface for a table, that is composed of an * element structure where the child elements of the element * this view is responsible for represent rows and the child * elements of the row elements are cells.  The cell elements can * have an arbitrary element structure under them, which will * be built with the ViewFactory returned by the getViewFactory * method. * <pre> * * &nbsp;  TABLE * &nbsp;    ROW * &nbsp;      CELL * &nbsp;      CELL * &nbsp;    ROW * &nbsp;      CELL * &nbsp;      CELL * * </pre> * <p> * This is implemented as a hierarchy of boxes, the table itself * is a vertical box, the rows are horizontal boxes, and the cells * are vertical boxes.  The cells are allowed to span multiple * columns and rows.  By default, the table can be thought of as * being formed over a grid (i.e. somewhat like one would find in * gridbag layout), where table cells can request to span more * than one grid cell.  The default horizontal span of table cells * will be based upon this grid, but can be changed by reimplementing * the requested span of the cell (i.e. table cells can have independant * spans if desired). * * @author  Timothy Prinzing * @see     View */public abstract class TableView extends BoxView {     /**     * Constructs a TableView for the given element.     *     * @param elem the element that this view is responsible for     */    public TableView(Element elem) {        super(elem, View.Y_AXIS);        rows = new Vector<TableRow>();        gridValid = false;    }     /**     * Creates a new table row.     *     * @param elem an element     * @return the row     */    protected TableRow createTableRow(Element elem) {        return new TableRow(elem);    }     /**     * @deprecated Table cells can now be any arbitrary     * View implementation and should be produced by the     * ViewFactory rather than the table.     *     * @param elem an element     * @return the cell     */    @Deprecated    protected TableCell createTableCell(Element elem) {        return new TableCell(elem);    }     /**     * The number of columns in the table.     */    int getColumnCount() {        return columnSpans.length;    }     /**     * Fetches the span (width) of the given column.     * This is used by the nested cells to query the     * sizes of grid locations outside of themselves.     */    int getColumnSpan(int col) {        return columnSpans[col];    }     /**     * The number of rows in the table.     */    int getRowCount() {        return rows.size();    }     /**     * Fetches the span (height) of the given row.     */    int getRowSpan(int row) {        View rv = getRow(row);        if (rv != null) {            return (int) rv.getPreferredSpan(Y_AXIS);        }        return 0;    }     TableRow getRow(int row) {        if (row < rows.size()) {            return rows.elementAt(row);        }        return null;    }     /**     * Determines the number of columns occupied by     * the table cell represented by given element.     */    /*protected*/ int getColumnsOccupied(View v) {        // PENDING(prinz) this code should be in the html        // paragraph, but we can't add api to enable it.        AttributeSet a = v.getElement().getAttributes();        String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);        if (s != null) {            try {                return Integer.parseInt(s);            } catch (NumberFormatException nfe) {                // fall through to one column            }        }         return 1;    }     /**     * Determines the number of rows occupied by     * the table cell represented by given element.     */    /*protected*/ int getRowsOccupied(View v) {        // PENDING(prinz) this code should be in the html        // paragraph, but we can't add api to enable it.        AttributeSet a = v.getElement().getAttributes();        String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);        if (s != null) {            try {                return Integer.parseInt(s);            } catch (NumberFormatException nfe) {                // fall through to one row            }        }         return 1;    }     /*protected*/ void invalidateGrid() {        gridValid = false;    }     protected void forwardUpdate(DocumentEvent.ElementChange ec,                                     DocumentEvent e, Shape a, ViewFactory f) {        super.forwardUpdate(ec, e, a, f);        // A change in any of the table cells usually effects the whole table,        // so redraw it all!        if (a != null) {            Component c = getContainer();            if (c != null) {                Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :                                   a.getBounds();                c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);            }        }    }     /**     * Change the child views.  This is implemented to     * provide the superclass behavior and invalidate the     * grid so that rows and columns will be recalculated.     */    public void replace(int offset, int length, View[] views) {        super.replace(offset, length, views);        invalidateGrid();    }     /**     * Fill in the grid locations that are placeholders     * for multi-column, multi-row, and missing grid     * locations.     */    void updateGrid() {        if (! gridValid) {            // determine which views are table rows and clear out            // grid points marked filled.            rows.removeAllElements();            int n = getViewCount();            for (int i = 0; i < n; i++) {                View v = getView(i);                if (v instanceof TableRow) {                    rows.addElement((TableRow) v);                    TableRow rv = (TableRow) v;                    rv.clearFilledColumns();                    rv.setRow(i);                }            }             int maxColumns = 0;            int nrows = rows.size();            for (int row = 0; row < nrows; row++) {                TableRow rv = getRow(row);                int col = 0;                for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {                    View cv = rv.getView(cell);                    // advance to a free column                    for (; rv.isFilled(col); col++);                    int rowSpan = getRowsOccupied(cv);                    int colSpan = getColumnsOccupied(cv);                    if ((colSpan > 1) || (rowSpan > 1)) {                        // fill in the overflow entries for this cell                        int rowLimit = row + rowSpan;                        int colLimit = col + colSpan;                        for (int i = row; i < rowLimit; i++) {                            for (int j = col; j < colLimit; j++) {                                if (i != row || j != col) {                                    addFill(i, j);                                }                            }                        }                        if (colSpan > 1) {                            col += colSpan - 1;                        }                    }                }                maxColumns = Math.max(maxColumns, col);            }             // setup the column layout/requirements            columnSpans = new int[maxColumns];            columnOffsets = new int[maxColumns];            columnRequirements = new SizeRequirements[maxColumns];            for (int i = 0; i < maxColumns; i++) {                columnRequirements[i] = new SizeRequirements();            }            gridValid = true;        }    }     /**     * Mark a grid location as filled in for a cells overflow.     */    void addFill(int row, int col) {        TableRow rv = getRow(row);        if (rv != null) {            rv.fillColumn(col);        }    }     /**     * Lays out the columns to fit within the given target span.     * Returns the results through {@code offsets} and {@code spans}.     *     * @param targetSpan the given span for total of all the table     *  columns     * @param reqs the requirements desired for each column.  This     *  is the column maximum of the cells minimum, preferred, and     *  maximum requested span     * @param spans the return value of how much to allocated to     *  each column     * @param offsets the return value of the offset from the     *  origin for each column     */    protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,                                 SizeRequirements[] reqs) {        // allocate using the convenience method on SizeRequirements        SizeRequirements.calculateTiledPositions(targetSpan, null, reqs,                                                 offsets, spans);    }     /**     * Perform layout for the minor axis of the box (i.e. the     * axis orthoginal to the axis that it represents).  The results     * of the layout should be placed in the given arrays which represent     * the allocations to the children along the minor axis.  This     * is called by the superclass whenever the layout needs to be     * updated along the minor axis.     * <p>     * This is implemented to call the     * {@link #layoutColumns layoutColumns} method, and then     * forward to the superclass to actually carry out the layout     * of the tables rows.     *     * @param targetSpan the total span given to the view, which     *  whould be used to layout the children.     * @param axis the axis being layed out.     * @param offsets the offsets from the origin of the view for     *  each of the child views.  This is a return value and is     *  filled in by the implementation of this method.     * @param spans the span of each child view.  This is a return     *  value and is filled in by the implementation of this method.     */    protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {        // make grid is properly represented        updateGrid();         // all of the row layouts are invalid, so mark them that way        int n = getRowCount();        for (int i = 0; i < n; i++) {            TableRow row = getRow(i);            row.layoutChanged(axis);        }         // calculate column spans        layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);         // continue normal layout        super.layoutMinorAxis(targetSpan, axis, offsets, spans);    }     /**     * Calculate the requirements for the minor axis.  This is called by     * the superclass whenever the requirements need to be updated (i.e.     * a preferenceChanged was messaged through this view).     * <p>     * This is implemented to calculate the requirements as the sum of the     * requirements of the columns.     */    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {        updateGrid();         // calculate column requirements for each column        calculateColumnRequirements(axis);          // the requirements are the sum of the columns.        if (r == null) {            r = new SizeRequirements();        }        long min = 0;        long pref = 0;        long max = 0;        for (SizeRequirements req : columnRequirements) {            min += req.minimum;            pref += req.preferred;            max += req.maximum;        }        r.minimum = (int) min;        r.preferred = (int) pref;        r.maximum = (int) max;        r.alignment = 0;        return r;    }     /*    boolean shouldTrace() {        AttributeSet a = getElement().getAttributes();        Object o = a.getAttribute(HTML.Attribute.ID);        if ((o != null) && o.equals("debug")) {            return true;        }        return false;    }    */     /**     * Calculate the requirements for each column.  The calculation     * is done as two passes over the table.  The table cells that     * occupy a single column are scanned first to determine the     * maximum of minimum, preferred, and maximum spans along the     * give axis.  Table cells that span multiple columns are excluded     * from the first pass.  A second pass is made to determine if     * the cells that span multiple columns are satisfied.  If the     * column requirements are not satisified, the needs of the     * multi-column cell is mixed into the existing column requirements.     * The calculation of the multi-column distribution is based upon     * the proportions of the existing column requirements and taking     * into consideration any constraining maximums.     */    void calculateColumnRequirements(int axis) {        // pass 1 - single column cells        boolean hasMultiColumn = false;        int nrows = getRowCount();        for (int i = 0; i < nrows; i++) {            TableRow row = getRow(i);            int col = 0;            int ncells = row.getViewCount();            for (int cell = 0; cell < ncells; cell++, col++) {                View cv = row.getView(cell);                for (; row.isFilled(col); col++); // advance to a free column                int rowSpan = getRowsOccupied(cv);                int colSpan = getColumnsOccupied(cv);                if (colSpan == 1) {                    checkSingleColumnCell(axis, col, cv);                } else {                    hasMultiColumn = true;                    col += colSpan - 1;                }            }        }         // pass 2 - multi-column cells        if (hasMultiColumn) {            for (int i = 0; i < nrows; i++) {                TableRow row = getRow(i);                int col = 0;                int ncells = row.getViewCount();                for (int cell = 0; cell < ncells; cell++, col++) {                    View cv = row.getView(cell);                    for (; row.isFilled(col); col++); // advance to a free column                    int colSpan = getColumnsOccupied(cv);                    if (colSpan > 1) {                        checkMultiColumnCell(axis, col, colSpan, cv);                        col += colSpan - 1;                    }                }            }        }         /*        if (shouldTrace()) {            System.err.println("calc:");            for (int i = 0; i < columnRequirements.length; i++) {                System.err.println(" " + i + ": " + columnRequirements[i]);            }        }        */    }     /**     * check the requirements of a table cell that spans a single column.     */    void checkSingleColumnCell(int axis, int col, View v) {        SizeRequirements req = columnRequirements[col];        req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);        req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);        req.maximum = Math.max((int) v.getMaximumSpan(axis), req.maximum);    }     /**     * check the requirements of a table cell that spans multiple     * columns.     */    void checkMultiColumnCell(int axis, int col, int ncols, View v) {        // calculate the totals        long min = 0;        long pref = 0;        long max = 0;        for (int i = 0; i < ncols; i++) {            SizeRequirements req = columnRequirements[col + i];            min += req.minimum;            pref += req.preferred;            max += req.maximum;        }         // check if the minimum size needs adjustment.        int cmin = (int) v.getMinimumSpan(axis);        if (cmin > min) {            /*             * the columns that this cell spans need adjustment to fit             * this table cell.... calculate the adjustments.  The             * maximum for each cell is the maximum of the existing             * maximum or the amount needed by the cell.             */            SizeRequirements[] reqs = new SizeRequirements[ncols];            for (int i = 0; i < ncols; i++) {                SizeRequirements r = reqs[i] = columnRequirements[col + i];                r.maximum = Math.max(r.maximum, (int) v.getMaximumSpan(axis));            }            int[] spans = new int[ncols];            int[] offsets = new int[ncols];            SizeRequirements.calculateTiledPositions(cmin, null, reqs,

Lines 1–500 of 907.