All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.pivot.wtk.TablePane Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you 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 org.apache.pivot.wtk;

import java.util.Iterator;

import org.apache.pivot.beans.DefaultProperty;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.util.ImmutableIterator;
import org.apache.pivot.util.ListenerList;

/**
 * Container that arranges components in a two-dimensional grid, optionally
 * spanning multiple rows and columns, much like an HTML <table>
 * element.
 * 

* Note that unlike an HTML <table>, components that span * multiple rows or columns will not "push" other components out of their way. * Instead, the spanning components will simply overlay the cells into which * they span. This means that application developers may have to use * {@link Filler filler cells} in the cells that are spanned. */ @DefaultProperty("rows") public class TablePane extends Container { /** * Represents a table pane row. */ public static class Row implements Sequence, Iterable { private int height; private boolean relative; private boolean highlighted; private ArrayList cells = new ArrayList(); private TablePane tablePane = null; public Row() { this(-1, false, false); } public Row(int height) { this(height, false, false); } public Row(int height, boolean relative) { this(height, relative, false); } public Row(int height, boolean relative, boolean highlighted) { this.height = height; this.relative = relative; this.highlighted = highlighted; } /** * Returns the table pane with which this row is associated. * * @return * The row's table pane, or null if the row does not * currently belong to a table. */ public TablePane getTablePane() { return tablePane; } /** * Returns the row height. * * @return * The height of the row. */ public int getHeight() { return height; } /** * Returns the relative flag. * * @return * true if the row height is relative, false if it * is fixed. */ public boolean isRelative() { return relative; } /** * Set the row height. * * @param height * The absolute height of the row. */ public void setHeight(int height) { setHeight(height, false); } /** * Set the row height. * * @param height * The encoded height of the row. If the string ends with the '*' * character, it is treated as a relative value. Otherwise, it is * considered an absolute value. */ public void setHeight(String height) { boolean relativeLocal = false; if (height.endsWith(RELATIVE_SIZE_INDICATOR)) { relativeLocal = true; setHeight(Integer.parseInt(height.substring(0, height.length() - 1)), relativeLocal); } else { setHeight(Integer.parseInt(height), relativeLocal); } } /** * Sets the row height. * * @param height * The height of the row. * * @param relative * true if the row height is relative, false if it * is fixed. */ public void setHeight(int height, boolean relative) { int previousHeight = this.height; boolean previousRelative = this.relative; if (previousHeight != height || previousRelative != relative) { this.height = height; this.relative = relative; if (tablePane != null) { tablePane.tablePaneListeners.rowHeightChanged(this, previousHeight, previousRelative); } } } /** * Returns the highlighted flag. * * @return * true if the row is highlighted, false if it is not */ public boolean isHighlighted() { return highlighted; } /** * Sets the highlighted flag. * * @param highlighted * true to set the row as highlighted, false to set * it as not highlighted */ public void setHighlighted(boolean highlighted) { if (highlighted != this.highlighted) { this.highlighted = highlighted; if (tablePane != null) { tablePane.tablePaneListeners.rowHighlightedChanged(this); } } } @Override public int add(Component component) { int index = getLength(); insert(component, index); return index; } @Override public void insert(Component component, int index) { if (component == null) { throw new IllegalArgumentException("Component is null."); } if (component.getParent() != null) { throw new IllegalArgumentException("Component already has a parent."); } cells.insert(component, index); if (tablePane != null) { tablePane.add(component); tablePane.tablePaneListeners.cellInserted(this, index); } } @Override public Component update(int index, Component component) { Component previousComponent = cells.get(index); if (component != previousComponent) { if (component == null) { throw new IllegalArgumentException("Component is null."); } if (component.getParent() != null) { throw new IllegalArgumentException("Component already has a parent."); } cells.update(index, component); if (tablePane != null) { tablePane.add(component); tablePane.tablePaneListeners.cellUpdated(this, index, previousComponent); tablePane.remove(previousComponent); } } return previousComponent; } @Override public int remove(Component component) { int index = indexOf(component); if (index != -1) { remove(index, 1); } return index; } @Override public Sequence remove(int index, int count) { Sequence removed = cells.remove(index, count); if (tablePane != null) { tablePane.tablePaneListeners.cellsRemoved(this, index, removed); for (int i = 0, n = removed.getLength(); i < n; i++) { Component component = removed.get(i); tablePane.remove(component); } } return removed; } @Override public Component get(int index) { return cells.get(index); } @Override public int indexOf(Component component) { return cells.indexOf(component); } @Override public int getLength() { return cells.getLength(); } @Override public Iterator iterator() { return new ImmutableIterator(cells.iterator()); } } /** * Represents a table pane column. */ public static class Column { private TablePane tablePane = null; private int width; private boolean relative; private boolean highlighted; public Column() { this(-1, false, false); } public Column(int width) { this(width, false, false); } public Column(int width, boolean relative) { this(width, relative, false); } public Column(int width, boolean relative, boolean highlighted) { this.width = width; this.relative = relative; this.highlighted = highlighted; } /** * Returns the table pane with which this column is associated. * * @return * The column's table pane, or null if the column does not * currently belong to a table. */ public TablePane getTablePane() { return tablePane; } /** * Returns the column width. * * @return * The width of the column. */ public int getWidth() { return width; } /** * Returns the relative flag. * * @return * true if the column width is relative, false if it * is fixed. */ public boolean isRelative() { return relative; } /** * Set the column width. * * @param width * The absolute width of the column. */ public void setWidth(int width) { setWidth(width, false); } /** * Set the column width. * * @param width * The encoded width of the row. If the string ends with the '*' * character, it is treated as a relative value. Otherwise, it is * considered an absolute value. */ public void setWidth(String width) { boolean relativeLocal = false; if (width.endsWith(RELATIVE_SIZE_INDICATOR)) { relativeLocal = true; setWidth(Integer.parseInt(width.substring(0, width.length() - 1)), relativeLocal); } else { setWidth(Integer.parseInt(width), relativeLocal); } } /** * Sets the column width. * * @param width * The width of the column. * * @param relative * true if the column width is relative, false if it * is fixed. */ public void setWidth(int width, boolean relative) { int previousWidth = this.width; boolean previousRelative = this.relative; if (previousWidth != width || previousRelative != relative) { this.width = width; this.relative = relative; if (tablePane != null) { tablePane.tablePaneListeners.columnWidthChanged(this, previousWidth, previousRelative); } } } /** * Returns the highlighted flag. * * @return * true if the column is highlighted, false if it is not */ public boolean isHighlighted() { return highlighted; } /** * Sets the highlighted flag. * * @param highlighted * true to set the column as highlighted, false to set * it as not highlighted */ public void setHighlighted(boolean highlighted) { if (highlighted != this.highlighted) { this.highlighted = highlighted; if (tablePane != null) { tablePane.tablePaneListeners.columnHighlightedChanged(this); } } } } /** * Table pane skin interface. Table pane skins must implement * this interface to facilitate additional communication between the * component and the skin. */ public interface Skin { public int getRowAt(int y); public Bounds getRowBounds(int row); public int getColumnAt(int x); public Bounds getColumnBounds(int column); } /** * Class that manages a table pane's row list. Callers get access to the * row sequence via {@link TablePane#getRows()}. */ public final class RowSequence implements Sequence, Iterable { private RowSequence() { } @Override public int add(Row row) { int index = getLength(); insert(row, index); return index; } @Override public void insert(Row row, int index) { if (row == null) { throw new IllegalArgumentException("row is null."); } if (row.tablePane != null) { throw new IllegalArgumentException ("row is already in use by another table pane."); } rows.insert(row, index); row.tablePane = TablePane.this; for (int i = 0, n = row.getLength(); i < n; i++) { Component component = row.get(i); TablePane.this.add(component); } // Notify listeners tablePaneListeners.rowInserted(TablePane.this, index); } @Override public Row update(int index, Row row) { throw new UnsupportedOperationException(); } @Override public int remove(Row row) { int index = indexOf(row); if (index != -1) { remove(index, 1); } return index; } @Override public Sequence remove(int index, int count) { Sequence removed = rows.remove(index, count); if (count > 0) { for (int i = 0, n = removed.getLength(); i < n; i++) { Row row = removed.get(i); row.tablePane = null; for (int j = 0, m = row.getLength(); j < m; j++) { Component component = row.get(j); TablePane.this.remove(component); } } tablePaneListeners.rowsRemoved(TablePane.this, index, removed); } return removed; } @Override public Row get(int index) { return rows.get(index); } @Override public int indexOf(Row row) { return rows.indexOf(row); } @Override public int getLength() { return rows.getLength(); } @Override public Iterator iterator() { return new ImmutableIterator(rows.iterator()); } } /** * Class that manages a table pane's column list. Callers get access to the * column sequence via {@link TablePane#getColumns()}. */ public final class ColumnSequence implements Sequence, Iterable { private ColumnSequence() { } @Override public int add(Column column) { int index = getLength(); insert(column, index); return index; } @Override public void insert(Column column, int index) { if (column == null) { throw new IllegalArgumentException("column is null."); } if (column.tablePane != null) { throw new IllegalArgumentException ("column is already in use by another table pane."); } columns.insert(column, index); column.tablePane = TablePane.this; // Notify listeners tablePaneListeners.columnInserted(TablePane.this, index); } @Override public Column update(int index, Column column) { throw new UnsupportedOperationException(); } @Override public int remove(Column column) { int index = indexOf(column); if (index != -1) { remove(index, 1); } return index; } @Override public Sequence remove(int index, int count) { Sequence removed = columns.remove(index, count); if (count > 0) { for (int i = 0, n = removed.getLength(); i < n; i++) { Column column = removed.get(i); column.tablePane = null; } tablePaneListeners.columnsRemoved(TablePane.this, index, removed); } return removed; } @Override public Column get(int index) { return columns.get(index); } @Override public int indexOf(Column column) { return columns.indexOf(column); } @Override public int getLength() { return columns.getLength(); } @Override public Iterator iterator() { return new ImmutableIterator(columns.iterator()); } } /** * Component that can be used as filler for empty cells. */ public static final class Filler extends Component { public Filler() { installSkin(Filler.class); } } private enum Attribute { ROW_SPAN, COLUMN_SPAN; } private static class TablePaneListenerList extends WTKListenerList implements TablePaneListener { @Override public void rowInserted(TablePane tablePane, int index) { for (TablePaneListener listener : this) { listener.rowInserted(tablePane, index); } } @Override public void rowsRemoved(TablePane tablePane, int index, Sequence rows) { for (TablePaneListener listener : this) { listener.rowsRemoved(tablePane, index, rows); } } @Override public void rowHeightChanged(TablePane.Row row, int previousHeight, boolean previousRelative) { for (TablePaneListener listener : this) { listener.rowHeightChanged(row, previousHeight, previousRelative); } } @Override public void rowHighlightedChanged(TablePane.Row row) { for (TablePaneListener listener : this) { listener.rowHighlightedChanged(row); } } @Override public void columnInserted(TablePane tablePane, int index) { for (TablePaneListener listener : this) { listener.columnInserted(tablePane, index); } } @Override public void columnsRemoved(TablePane tablePane, int index, Sequence columns) { for (TablePaneListener listener : this) { listener.columnsRemoved(tablePane, index, columns); } } @Override public void columnWidthChanged(TablePane.Column column, int previousWidth, boolean previousRelative) { for (TablePaneListener listener : this) { listener.columnWidthChanged(column, previousWidth, previousRelative); } } @Override public void columnHighlightedChanged(TablePane.Column column) { for (TablePaneListener listener : this) { listener.columnHighlightedChanged(column); } } @Override public void cellInserted(TablePane.Row row, int column) { for (TablePaneListener listener : this) { listener.cellInserted(row, column); } } @Override public void cellsRemoved(TablePane.Row row, int column, Sequence removed) { for (TablePaneListener listener : this) { listener.cellsRemoved(row, column, removed); } } @Override public void cellUpdated(TablePane.Row row, int column, Component previousComponent) { for (TablePaneListener listener : this) { listener.cellUpdated(row, column, previousComponent); } } } private static class TablePaneAttributeListenerList extends WTKListenerList implements TablePaneAttributeListener { @Override public void rowSpanChanged(TablePane tablePane, Component component, int previousRowSpan) { for (TablePaneAttributeListener listener : this) { listener.rowSpanChanged(tablePane, component, previousRowSpan); } } @Override public void columnSpanChanged(TablePane tablePane, Component component, int previousColumnSpan) { for (TablePaneAttributeListener listener : this) { listener.columnSpanChanged(tablePane, component, previousColumnSpan); } } } private ArrayList rows = null; private RowSequence rowSequence = new RowSequence(); private ArrayList columns = null; private ColumnSequence columnSequence = new ColumnSequence(); private TablePaneListenerList tablePaneListeners = new TablePaneListenerList(); private TablePaneAttributeListenerList tablePaneAttributeListeners = new TablePaneAttributeListenerList(); public static final String RELATIVE_SIZE_INDICATOR = "*"; /** * Creates a new TablePane with empty row and column sequences. */ public TablePane() { this(new ArrayList()); } /** * Creates a new TablePane with the specified columns. * * @param columns * The column sequence to use. A copy of this sequence will be made */ public TablePane(Sequence columns) { if (columns == null) { throw new IllegalArgumentException("columns is null"); } this.rows = new ArrayList(); this.columns = new ArrayList(columns); installSkin(TablePane.class); } @Override protected void setSkin(org.apache.pivot.wtk.Skin skin) { if (!(skin instanceof TablePane.Skin)) { throw new IllegalArgumentException("Skin class must implement " + TablePane.Skin.class.getName()); } super.setSkin(skin); } /** * Returns the table pane row sequence. * * @return * The table pane row sequence */ public RowSequence getRows() { return rowSequence; } /** * Returns the index of the row at a given location. * * @param y * The y-coordinate of the row to identify. * * @return * The row index, or -1 if there is no row at the given * y-coordinate. */ public int getRowAt(int y) { TablePane.Skin tablePaneSkin = (TablePane.Skin)getSkin(); return tablePaneSkin.getRowAt(y); } /** * Returns the bounds of a given row. * * @param row * The row index. */ public Bounds getRowBounds(int row) { TablePane.Skin tablePaneSkin = (TablePane.Skin)getSkin(); return tablePaneSkin.getRowBounds(row); } /** * Returns the table pane column sequence. * * @return * The table pane column sequence */ public ColumnSequence getColumns() { return columnSequence; } /** * Returns the index of the column at a given location. * * @param x * The x-coordinate of the column to identify. * * @return * The column index, or -1 if there is no column at the given * x-coordinate. */ public int getColumnAt(int x) { TablePane.Skin tablePaneSkin = (TablePane.Skin)getSkin(); return tablePaneSkin.getColumnAt(x); } /** * Returns the bounds of a given column. * * @param column * The column index. */ public Bounds getColumnBounds(int column) { TablePane.Skin tablePaneSkin = (TablePane.Skin)getSkin(); return tablePaneSkin.getColumnBounds(column); } /** * Gets the component at the specified cell in this table pane. * * @param rowIndex * The row index of the cell * * @param columnIndex * The column index of the cell * * @return * The component in the specified cell, or null if the cell is * empty */ public Component getCellComponent(int rowIndex, int columnIndex) { Row row = rows.get(rowIndex); Component component = null; if (row.getLength() > columnIndex) { component = row.get(columnIndex); } return component; } /** * Overrides the base method to check whether or not a cell component is * being removed, and fires the appropriate event in that case. * * @param index * The index at which components were removed * * @param count * The number of components removed * * @return * The sequence of components that were removed */ @Override public Sequence remove(int index, int count) { for (int i = index, n = index + count; i < n; i++) { Component component = get(i); for (Row row : rows) { if (row.indexOf(component) >= 0) { throw new UnsupportedOperationException(); } } } // Call the base method to remove the components return super.remove(index, count); } /** * Returns the table pane listener list. */ public ListenerList getTablePaneListeners() { return tablePaneListeners; } /** * Returns the table pane attribute listener list. */ public ListenerList getTablePaneAttributeListeners() { return tablePaneAttributeListeners; } public static int getRowSpan(Component component) { Integer value = (Integer)component.getAttribute(Attribute.ROW_SPAN); return (value == null) ? 1 : value; } public static void setRowSpan(Component component, int rowSpan) { Integer previousValue = (Integer)component.setAttribute(Attribute.ROW_SPAN, rowSpan); int previousRowSpan = (previousValue == null) ? 1 : previousValue; if (previousRowSpan != rowSpan) { Container parent = component.getParent(); if (parent instanceof TablePane) { TablePane tablePane = (TablePane)parent; tablePane.tablePaneAttributeListeners.rowSpanChanged(tablePane, component, previousRowSpan); } } } public static int getColumnSpan(Component component) { Integer value = (Integer)component.getAttribute(Attribute.COLUMN_SPAN); return (value == null) ? 1 : value; } public static void setColumnSpan(Component component, int columnSpan) { Integer previousValue = (Integer)component.setAttribute(Attribute.COLUMN_SPAN, columnSpan); int previousColumnSpan = (previousValue == null) ? 1 : previousValue; if (previousColumnSpan != columnSpan) { Container parent = component.getParent(); if (parent instanceof TablePane) { TablePane tablePane = (TablePane)parent; tablePane.tablePaneAttributeListeners.columnSpanChanged(tablePane, component, previousColumnSpan); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy