0001: /*
0002: #IFNDEF ALT_LICENSE
0003: ThinWire(R) RIA Ajax Framework
0004: Copyright (C) 2003-2007 Custom Credit Systems
0005:
0006: This library is free software; you can redistribute it and/or modify it under
0007: the terms of the GNU Lesser General Public License as published by the Free
0008: Software Foundation; either version 2.1 of the License, or (at your option) any
0009: later version.
0010:
0011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
0012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
0014:
0015: You should have received a copy of the GNU Lesser General Public License along
0016: with this library; if not, write to the Free Software Foundation, Inc., 59
0017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
0018:
0019: Users who would rather have a commercial license, warranty or support should
0020: contact the following company who invented, built and supports the technology:
0021:
0022: Custom Credit Systems, Richardson, TX 75081, USA.
0023: email: info@thinwire.com ph: +1 (888) 644-6405
0024: http://www.thinwire.com
0025: #ENDIF
0026: [ v1.2_RC2 ]
0027: */
0028: package thinwire.ui.layout;
0029:
0030: import java.util.ArrayList;
0031: import java.util.Collections;
0032: import java.util.Comparator;
0033: import java.util.HashMap;
0034: import java.util.Iterator;
0035: import java.util.List;
0036: import java.util.Map;
0037: import java.util.SortedSet;
0038: import java.util.TreeSet;
0039: import java.util.logging.Logger;
0040:
0041: import thinwire.ui.Component;
0042: import thinwire.ui.Container;
0043: import thinwire.ui.event.ItemChangeEvent;
0044: import thinwire.ui.event.ItemChangeEvent.Type;
0045: import thinwire.util.ArrayGrid;
0046: import thinwire.util.Grid;
0047:
0048: /**
0049: * A layout that manages Components in a table structure that is backed by a Grid.
0050: * You can use this rapidly and easily layout user interfaces of almost any complexity.
0051: * Additionaly, since this layout is backed by a Grid, you can dynamically manipulate
0052: * the layout to accomplish sophisticated user interfaces.
0053: * <p>
0054: * The general concepts of this layout are adapted directly from the TableLayout
0055: * project at http://tablelayout.dev.java.net/. While, the TableLayout project
0056: * at java.net is for the Java Swing UI framework, the basic idea behind the
0057: * layout is applicable to any user interface framework. In general, you can
0058: * use a good portion of the documentation from that project to guide you in
0059: * using this layout.
0060: * </p>
0061: * <p>
0062: * There are two primary differences to keep in mind. First,
0063: * ThinWire layout's use 'limits', whereas Swing layouts use 'constraints'. The
0064: * concept is the same, but in ThinWire you set a limit by calling the "setLimit()"
0065: * method on a Component. Second, since this layout is backed by a ThinWire
0066: * Grid, row/column additions and removals are done entirely different then
0067: * how the TableLayout project handles it.
0068: * </p>
0069: * <p>
0070: * With this layout, almost any Grid operation that the GridBox component supports
0071: * can be performed. This gives you extensive flexibility in how forms are laid
0072: * out and manipulated. Essentially, you can treat a TableLayout that's been
0073: * set on a Panel as an extremely flexible GridBox. It takes a little more setup
0074: * then a GridBox because you have to create instances of the components you want
0075: * to display in each cell. Additionally, the overall performance will be lower
0076: * if you have 100's of rows of information. However, in exchange for those issues
0077: * you can use this layout to pull off complex data grids that approach the capability
0078: * of a modern spreadsheet.
0079: * </p>
0080: * @author Joshua J. Gertzen
0081: * @author Ted C. Howard
0082: */
0083: public final class TableLayout extends AbstractLayout implements
0084: Grid<TableLayout.Row, TableLayout.Column> {
0085: private static final Logger log = Logger
0086: .getLogger(TableLayout.class.getName());
0087:
0088: public enum Justify {
0089: CENTER, CENTER_BOTTOM, CENTER_FULL, CENTER_TOP, FULL, FULL_CENTER, LEFT_BOTTOM, LEFT_CENTER, LEFT_FULL, LEFT_TOP, RIGHT_BOTTOM, RIGHT_CENTER, RIGHT_FULL, RIGHT_TOP
0090: };
0091:
0092: public static final class Range {
0093: private int columnIndex;
0094: private int rowIndex;
0095: private TableLayout layout;
0096: private int columnSpan;
0097: private int rowSpan;
0098: private String stringValue;
0099: private Justify justify;
0100:
0101: public Range(TableLayout layout, String range) {
0102: this (layout, range, (Range) null);
0103: }
0104:
0105: public Range(TableLayout layout, String range,
0106: Component relativeTo) {
0107: this (layout, range, (Range) layout
0108: .getFormalLimit(relativeTo));
0109: }
0110:
0111: public Range(TableLayout layout, String range, String relativeTo) {
0112: this (layout, range, new Range(layout, relativeTo));
0113: }
0114:
0115: public Range(TableLayout layout, String range, Range relativeTo) {
0116: if (layout == null)
0117: throw new IllegalArgumentException("layout == null");
0118: String[] values = ((String) range).split("\\s*,\\s*");
0119: Justify just = Justify.FULL;
0120: int width = 1;
0121: int height = 1;
0122: if (values.length >= 3) {
0123: if (values[2].equals("l")) {
0124: values[2] = "left";
0125: } else if (values[2].equals("r")) {
0126: values[2] = "right";
0127: } else if (values[2].equals("c")) {
0128: values[2] = "center";
0129: } else if (values[2].equals("f")) {
0130: values[2] = "full";
0131: }
0132:
0133: if (values[3].equals("t")) {
0134: values[3] = "top";
0135: } else if (values[3].equals("b")) {
0136: values[3] = "bottom";
0137: } else if (values[3].equals("c")) {
0138: values[3] = "center";
0139: } else if (values[3].equals("f")) {
0140: values[3] = "full";
0141: }
0142:
0143: try {
0144: String justStr = values[2].equals(values[3]) ? values[2]
0145: : values[2] + "_" + values[3];
0146: just = Justify.valueOf(justStr.toUpperCase());
0147: } catch (IllegalArgumentException e) {
0148: width = Integer.parseInt(values[2]);
0149: height = Integer.parseInt(values[3]);
0150: }
0151:
0152: }
0153:
0154: int relColumn;
0155: int relRow;
0156:
0157: if (relativeTo == null) {
0158: relColumn = relRow = -1;
0159: } else {
0160: relColumn = relativeTo.getColumnIndex()
0161: + relativeTo.getColumnSpan() - 1;
0162: relRow = relativeTo.getRowIndex()
0163: + relativeTo.getRowSpan() - 1;
0164: }
0165:
0166: init(layout, values.length >= 1 ? getRelativeCoord(
0167: values[0], relColumn) : 0,
0168: values.length >= 2 ? getRelativeCoord(values[1],
0169: relRow) : 0, width, height, just);
0170: }
0171:
0172: private int getRelativeCoord(String coord, int relativeTo) {
0173: if (relativeTo == -1)
0174: return Integer.parseInt(coord);
0175: char op = coord.charAt(0);
0176:
0177: if (op == '-') {
0178: return relativeTo
0179: - Integer.parseInt(coord.substring(1));
0180: } else if (op == '+') {
0181: return relativeTo
0182: + Integer.parseInt(coord.substring(1));
0183: } else {
0184: return Integer.parseInt(coord);
0185: }
0186: }
0187:
0188: public Range(TableLayout layout, int column, int row) {
0189: init(layout, column, row, 1, 1, Justify.FULL);
0190: }
0191:
0192: public Range(TableLayout layout, int column, int row,
0193: int columnSpan, int rowSpan) {
0194: init(layout, column, row, columnSpan, rowSpan, Justify.FULL);
0195: }
0196:
0197: public Range(TableLayout layout, int column, int row,
0198: int columnSpan, int rowSpan, Justify justify) {
0199: init(layout, column, row, columnSpan, rowSpan, justify);
0200: }
0201:
0202: private void init(TableLayout layout, int column, int row,
0203: int columnSpan, int rowSpan, Justify justification) {
0204: if (layout == null)
0205: throw new IllegalArgumentException("layout == null");
0206: rangeCheck("column", column);
0207: rangeCheck("row", row);
0208: rangeCheck("columnSpan", columnSpan);
0209: rangeCheck("rowSpan", rowSpan);
0210: this .layout = layout;
0211: this .columnIndex = column;
0212: this .rowIndex = row;
0213: this .columnSpan = columnSpan;
0214: this .rowSpan = rowSpan;
0215: this .justify = justification;
0216: }
0217:
0218: private void rangeCheck(String name, int value) {
0219: if (value < 0 || value > Short.MAX_VALUE)
0220: throw new IllegalArgumentException(Range.class
0221: .getName()
0222: + "."
0223: + name
0224: + " < 0 || "
0225: + Range.class.getName()
0226: + "."
0227: + name
0228: + " > "
0229: + Short.MAX_VALUE);
0230: }
0231:
0232: public TableLayout getParent() {
0233: return layout;
0234: }
0235:
0236: public int getColumnIndex() {
0237: return columnIndex;
0238: }
0239:
0240: public int getRowIndex() {
0241: return rowIndex;
0242: }
0243:
0244: public Column getColumn() {
0245: if (layout == null || columnIndex < 0
0246: || columnIndex >= layout.getColumns().size())
0247: return null;
0248: return layout.getColumns().get(columnIndex);
0249: }
0250:
0251: public Row getRow() {
0252: if (layout == null || rowIndex < 0
0253: || rowIndex >= layout.getRows().size())
0254: return null;
0255: return layout.getRows().get(rowIndex);
0256: }
0257:
0258: public int getColumnSpan() {
0259: return columnSpan;
0260: }
0261:
0262: public int getRowSpan() {
0263: return rowSpan;
0264: }
0265:
0266: public Justify getJustify() {
0267: return justify;
0268: }
0269:
0270: public boolean equals(Object o) {
0271: return o instanceof Range
0272: && toString().equals(o.toString());
0273: }
0274:
0275: public int hashCode() {
0276: return toString().hashCode();
0277: }
0278:
0279: public String toString() {
0280: if (stringValue == null)
0281: stringValue = "TableLayout.Range{columnIndex:"
0282: + columnIndex + ",rowIndex:" + rowIndex
0283: + ",columnSpan:" + columnSpan + ",rowSpan:"
0284: + rowSpan + ",justify=" + justify + "}";
0285: return stringValue;
0286: }
0287: }
0288:
0289: public static final class Row extends ArrayGrid.Row {
0290: private double height;
0291: private boolean visible;
0292:
0293: public Row() {
0294: this (0);
0295: }
0296:
0297: public Row(double height) {
0298: super ();
0299: this .height = height;
0300: this .visible = true;
0301: }
0302:
0303: public double getHeight() {
0304: return height;
0305: }
0306:
0307: public void setHeight(double height) {
0308: this .height = height;
0309: TableLayout layout = (TableLayout) getParent();
0310: if (layout != null && layout.isAutoApply())
0311: layout.apply();
0312: }
0313:
0314: public boolean isVisible() {
0315: return visible;
0316: }
0317:
0318: public void setVisible(boolean visible) {
0319: if (this .visible == visible)
0320: return;
0321: TableLayout layout = (TableLayout) getParent();
0322:
0323: if (layout != null && this .visible)
0324: layout.visibleRows.remove(this );
0325: this .visible = visible;
0326: if (layout != null && this .visible)
0327: layout.visibleRows.add(this );
0328:
0329: for (Object o : this ) {
0330: if (o instanceof Component) {
0331: ((Component) o).setVisible(this .visible);
0332: }
0333: }
0334:
0335: if (layout != null && layout.isAutoApply())
0336: layout.apply();
0337: }
0338: }
0339:
0340: public static final class Column extends ArrayGrid.Column {
0341: private double width;
0342: private boolean visible;
0343:
0344: public Column() {
0345: this (0);
0346: }
0347:
0348: public Column(double width) {
0349: super ();
0350: this .width = width;
0351: this .visible = true;
0352: }
0353:
0354: public double getWidth() {
0355: return width;
0356: }
0357:
0358: public void setWidth(double width) {
0359: this .width = width;
0360: TableLayout layout = (TableLayout) getParent();
0361: if (layout != null && layout.isAutoApply())
0362: layout.apply();
0363: }
0364:
0365: public boolean isVisible() {
0366: return visible;
0367: }
0368:
0369: public void setVisible(boolean visible) {
0370: if (this .visible == visible)
0371: return;
0372: TableLayout layout = (TableLayout) getParent();
0373: if (layout != null && this .visible)
0374: layout.visibleColumns.remove(this );
0375: this .visible = visible;
0376: if (layout != null && this .visible)
0377: layout.visibleColumns.add(this );
0378:
0379: for (Object o : this ) {
0380: if (o instanceof Component) {
0381: ((Component) o).setVisible(this .visible);
0382: }
0383: }
0384:
0385: if (layout != null && layout.isAutoApply())
0386: layout.apply();
0387: }
0388: }
0389:
0390: private ArrayGrid<Row, Column> grid;
0391: private SortedSet<Row> visibleRows;
0392: private SortedSet<Row> roVisibleRows;
0393: private SortedSet<Column> visibleColumns;
0394: private SortedSet<Column> roVisibleColumns;
0395: private boolean ignoreSet;
0396:
0397: public TableLayout(double sizes[][]) {
0398: this (sizes, 0, 0);
0399: }
0400:
0401: public TableLayout(double sizes[][], int margin) {
0402: this (sizes, margin, 0);
0403: }
0404:
0405: public TableLayout(double sizes[][], int margin, int spacing) {
0406: this ();
0407: if (sizes == null || sizes.length != 2)
0408: throw new IllegalArgumentException(
0409: "sizes == null || sizes.length != 2");
0410: if (sizes[0] == null || sizes[0].length == 0)
0411: throw new IllegalArgumentException(
0412: "sizes[0] == null || sizes[0].length == 0");
0413: if (sizes[1] == null || sizes[1].length == 0)
0414: throw new IllegalArgumentException(
0415: "sizes[1] == null || sizes[1].length == 0");
0416: ignoreSet = true;
0417: List<Column> columns = getColumns();
0418: for (double w : sizes[0])
0419: columns.add(new Column(w));
0420:
0421: List<Row> rows = getRows();
0422: for (double h : sizes[1])
0423: rows.add(new Row(h));
0424: setSpacing(spacing);
0425: setMargin(margin);
0426: ignoreSet = false;
0427: }
0428:
0429: public TableLayout() {
0430: super (Component.PROPERTY_LIMIT);
0431:
0432: ignoreSet = true;
0433: this .grid = new ArrayGrid<TableLayout.Row, TableLayout.Column>(
0434: this , TableLayout.Row.class, TableLayout.Column.class) {
0435: @Override
0436: protected void fireItemChange(Type type, int rowIndex,
0437: int columnIndex, Object oldValue, Object newValue) {
0438: List<Component> kids = TableLayout.this .getContainer() == null ? null
0439: : TableLayout.this .getContainer().getChildren();
0440:
0441: if (rowIndex >= 0 && columnIndex == -1) {
0442: TableLayout.this .setAutoApply(false);
0443:
0444: if (type == ItemChangeEvent.Type.ADD) {
0445: TableLayout.Row newRow = (TableLayout.Row) newValue;
0446: if (newRow.isVisible())
0447: TableLayout.this .visibleRows.add(newRow);
0448:
0449: if (!ignoreSet && kids != null) {
0450: for (Component c : kids) {
0451: Range l = (Range) c.getLimit();
0452:
0453: if (rowIndex <= l.getRowIndex()) {
0454: l.rowIndex++;
0455: } else if (rowIndex < (l.getRowIndex() + l
0456: .getRowSpan())) {
0457: l.rowSpan++;
0458: }
0459: }
0460:
0461: for (int i = 0, cnt = newRow.size(); i < cnt; i++) {
0462: Component c = (Component) newRow.get(i);
0463:
0464: if (c != null) {
0465: if (c.getLimit() == null)
0466: c.setLimit(new Range(
0467: TableLayout.this , i,
0468: rowIndex));
0469: kids.add(c);
0470: }
0471: }
0472: }
0473: } else if (type == ItemChangeEvent.Type.REMOVE) {
0474: if (getRows().size() == 0) { //Clear was called
0475: TableLayout.this .visibleRows.clear();
0476: if (kids != null)
0477: kids.clear();
0478: } else {
0479: if (((TableLayout.Row) oldValue)
0480: .isVisible())
0481: TableLayout.this .visibleRows
0482: .remove(oldValue);
0483:
0484: for (Iterator<Component> i = kids
0485: .iterator(); i.hasNext();) {
0486: Component c = i.next();
0487: Range r = (Range) c.getLimit();
0488: if (rowIndex == r.getRowIndex()) {
0489: r.rowIndex = -1;
0490: i.remove();
0491: } else if (rowIndex < r.getRowIndex()) {
0492: r.rowIndex--;
0493: } else if (rowIndex < (r.getRowIndex() + r
0494: .getRowSpan())) {
0495: r.rowSpan--;
0496: }
0497: }
0498: }
0499: } else if (type == ItemChangeEvent.Type.SET) {
0500: TableLayout.Row oldRow = (TableLayout.Row) oldValue;
0501: if (oldRow.isVisible())
0502: TableLayout.this .visibleRows.remove(oldRow);
0503:
0504: for (Iterator i = oldRow.iterator(); i
0505: .hasNext();) {
0506: Component c = (Component) i.next();
0507: kids.remove(c);
0508: }
0509:
0510: //TODO: Bug, for some reason the new row has nothing in it.
0511: //TableLayout.Row newRow = (TableLayout.Row) newValue;
0512: TableLayout.Row newRow = TableLayout.this
0513: .getRows().get(rowIndex);
0514: if (newRow.isVisible())
0515: TableLayout.this .visibleRows.add(newRow);
0516:
0517: for (int i = 0, cnt = newRow.size(); i < cnt; i++) {
0518: Component c = (Component) newRow.get(i);
0519: if (c.getLimit() == null)
0520: c.setLimit(new Range(TableLayout.this ,
0521: i, rowIndex));
0522: kids.add(c);
0523: }
0524: }
0525:
0526: TableLayout.this .setAutoApply(true);
0527: } else if (rowIndex == -1 && columnIndex >= 0) {
0528: TableLayout.this .setAutoApply(false);
0529:
0530: if (type == ItemChangeEvent.Type.ADD) {
0531: TableLayout.Column newColumn = (TableLayout.Column) newValue;
0532: if (newColumn.isVisible())
0533: TableLayout.this .visibleColumns
0534: .add(newColumn);
0535:
0536: if (!ignoreSet && kids != null) {
0537: for (Component c : kids) {
0538: Range r = (Range) c.getLimit();
0539: if (columnIndex <= r.getColumnIndex()) {
0540: r.columnIndex++;
0541: } else if (columnIndex < (r
0542: .getColumnIndex() + r
0543: .getColumnSpan())) {
0544: r.columnSpan++;
0545: }
0546: }
0547:
0548: for (int i = 0, cnt = newColumn.size(); i < cnt; i++) {
0549: Component c = (Component) newColumn
0550: .get(i);
0551: if (c != null) {
0552: if (c.getLimit() == null)
0553: c.setLimit(new Range(
0554: TableLayout.this ,
0555: columnIndex, i));
0556: kids.add(c);
0557: }
0558: }
0559: }
0560: } else if (type == ItemChangeEvent.Type.REMOVE) {
0561: if (((TableLayout.Column) oldValue).isVisible())
0562: TableLayout.this .visibleColumns
0563: .remove(oldValue);
0564:
0565: if (kids != null) {
0566: for (Iterator<Component> i = kids
0567: .iterator(); i.hasNext();) {
0568: Component c = i.next();
0569: Range r = (Range) c.getLimit();
0570: if (columnIndex == r.getColumnIndex()) {
0571: r.columnIndex = -1;
0572: i.remove();
0573: } else if (columnIndex < r
0574: .getColumnIndex()) {
0575: r.columnIndex--;
0576: } else if (columnIndex < (r
0577: .getColumnIndex() + r
0578: .getColumnSpan())) {
0579: r.columnSpan--;
0580: }
0581: }
0582: }
0583: } else if (type == ItemChangeEvent.Type.SET) {
0584: TableLayout.Column oldColumn = (TableLayout.Column) oldValue;
0585: if (oldColumn.isVisible())
0586: TableLayout.this .visibleColumns
0587: .remove(oldColumn);
0588:
0589: for (Iterator i = oldColumn.iterator(); i
0590: .hasNext();) {
0591: Component c = (Component) i.next();
0592: if (kids != null)
0593: kids.remove(c);
0594: }
0595:
0596: TableLayout.Column newColumn = (TableLayout.Column) newValue;
0597: if (newColumn.isVisible())
0598: TableLayout.this .visibleColumns
0599: .add(newColumn);
0600:
0601: for (int i = 0, cnt = newColumn.size(); i < cnt; i++) {
0602: Component c = (Component) newColumn.get(i);
0603: if (c.getLimit() == null)
0604: c.setLimit(new Range(TableLayout.this ,
0605: columnIndex, i));
0606: if (kids != null)
0607: kids.add(c);
0608: }
0609: }
0610:
0611: TableLayout.this .setAutoApply(true);
0612: } else if (rowIndex != -1 && columnIndex != -1
0613: && type == ItemChangeEvent.Type.SET) {
0614: if (!ignoreSet
0615: && (oldValue == null || !oldValue
0616: .equals(newValue))) {
0617:
0618: if (oldValue != null) {
0619: if (oldValue == newValue)
0620: return;
0621: kids.remove(oldValue);
0622: }
0623:
0624: if (newValue != null) {
0625: Component newComp = (Component) newValue;
0626: if (newComp.getLimit() == null)
0627: newComp.setLimit(new Range(
0628: TableLayout.this , columnIndex,
0629: rowIndex));
0630: if (kids != null)
0631: kids.add(newComp);
0632: }
0633: }
0634: }
0635: }
0636: };
0637:
0638: Comparator<Row> rowIndexOrder = new Comparator<Row>() {
0639: public int compare(Row r1, Row r2) {
0640: int index1 = r1.getIndex();
0641: int index2 = r2.getIndex();
0642:
0643: if (index1 < index2) {
0644: return -1;
0645: } else if (index1 == index2) {
0646: return 0;
0647: } else {
0648: return 1;
0649: }
0650: }
0651: };
0652:
0653: Comparator<Column> columnIndexOrder = new Comparator<Column>() {
0654: public int compare(Column c1, Column c2) {
0655: int index1 = c1.getIndex();
0656: int index2 = c2.getIndex();
0657:
0658: if (index1 < index2) {
0659: return -1;
0660: } else if (index1 == index2) {
0661: return 0;
0662: } else {
0663: return 1;
0664: }
0665: }
0666: };
0667:
0668: List<Row> rows = getRows();
0669: visibleRows = new TreeSet<Row>(rowIndexOrder);
0670: visibleRows.addAll(rows);
0671: roVisibleRows = Collections.unmodifiableSortedSet(visibleRows);
0672:
0673: List<Column> columns = getColumns();
0674: visibleColumns = new TreeSet<Column>(columnIndexOrder);
0675: visibleColumns.addAll(columns);
0676: roVisibleColumns = Collections
0677: .unmodifiableSortedSet(visibleColumns);
0678:
0679: setAutoApply(true);
0680: ignoreSet = false;
0681: }
0682:
0683: @Override
0684: protected void addComponent(Component comp) {
0685: addToLayout(comp, (Range) comp.getLimit());
0686: }
0687:
0688: private void addToLayout(Component comp, Range r) {
0689: int row = r.getRowIndex();
0690: int col = r.getColumnIndex();
0691: List<Row> rows = getRows();
0692: ignoreSet = true;
0693:
0694: for (int i = row, cnt = r.getRowSpan() + row; i < cnt; i++) {
0695: Row curRow = rows.get(i);
0696:
0697: for (int j = col, cnt2 = r.getColumnSpan() + col; j < cnt2; j++) {
0698: Object o = curRow.get(j);
0699:
0700: if (o == null) {
0701: curRow.set(j, comp);
0702: } else if (o instanceof List) {
0703: ((List<Component>) o).add(comp);
0704: } else if (o != comp) {
0705: List<Component> list = new ArrayList<Component>();
0706: list.add((Component) o);
0707: list.add(comp);
0708: curRow.set(j, list);
0709: }
0710: }
0711: }
0712:
0713: ignoreSet = false;
0714: }
0715:
0716: @Override
0717: protected void removeComponent(Component comp) {
0718: removeFromLayout(comp, (Range) comp.getLimit());
0719: }
0720:
0721: private void removeFromLayout(Component comp, Range r) {
0722: int row = r.getRowIndex();
0723: int col = r.getColumnIndex();
0724: int rowSpan = r.getRowSpan();
0725:
0726: //XXX JJG Why is this check necessary?
0727: if (row >= 0 && col >= 0) {
0728: ignoreSet = true;
0729: List<Row> rows = getRows();
0730: if (rows.size() > row + rowSpan) {
0731: for (int i = row, cnt = rowSpan + row; i < cnt; i++) {
0732: Row curRow = rows.get(i);
0733:
0734: for (int j = col, cnt2 = r.getColumnSpan() + col; j < cnt2; j++) {
0735: Object o = curRow.get(j);
0736:
0737: if (o instanceof Component) {
0738: curRow.set(j, null);
0739: } else if (o instanceof List) {
0740: ((List) o).remove(comp);
0741: }
0742: }
0743: }
0744: }
0745: ignoreSet = false;
0746: }
0747: }
0748:
0749: @Override
0750: protected void limitChanged(Component comp, Object oldLimit) {
0751: removeFromLayout(comp, (Range) oldLimit);
0752: addToLayout(comp, (Range) comp.getLimit());
0753: }
0754:
0755: @Override
0756: protected Object getFormalLimit(Component comp) {
0757: Object oLimit = comp.getLimit();
0758: Range r;
0759:
0760: if (oLimit instanceof String) {
0761: String sLimit = (String) oLimit;
0762:
0763: if (sLimit.indexOf('+') >= 0 || sLimit.indexOf('-') >= 0) {
0764: List<Component> kids = comp.getContainer()
0765: .getChildren();
0766: int index = kids.indexOf(comp);
0767:
0768: if (index > 0) {
0769: r = new Range(this , sLimit, kids.get(index - 1));
0770: } else {
0771: r = new Range(this , sLimit, new Range(this , 0, 0));
0772: }
0773: } else {
0774: r = new Range(this , sLimit);
0775: }
0776: } else if (oLimit instanceof Range) {
0777: r = (Range) oLimit;
0778: if (r.layout != this )
0779: r = new Range(this , r.getColumnIndex(),
0780: r.getRowIndex(), r.getColumnSpan(), r
0781: .getRowSpan(), r.getJustify());
0782: } else {
0783: r = new Range(this , 0, 0);
0784: }
0785:
0786: return r;
0787: }
0788:
0789: private int[] getAbsoluteSizes(int availableSize,
0790: SortedSet<? extends List> sizes) {
0791: int length = sizes.size();
0792: int[] absoluteSizes = new int[length];
0793: availableSize -= (length - 1) * spacing + margin * 2;
0794: int fillCnt = 0;
0795:
0796: for (List rc : sizes) {
0797: double f = rc instanceof Row ? ((Row) rc).getHeight()
0798: : ((Column) rc).getWidth();
0799: if (f >= 1)
0800: availableSize -= f;
0801: }
0802:
0803: int pctSize = availableSize;
0804: int i = 0;
0805:
0806: for (List o : sizes) {
0807: double size = o instanceof Row ? ((Row) o).getHeight()
0808: : ((Column) o).getWidth();
0809:
0810: if (size == 0) {
0811: fillCnt++;
0812: } else if (size < 1) {
0813: absoluteSizes[i] = (int) (pctSize * size);
0814: availableSize -= absoluteSizes[i];
0815: } else {
0816: absoluteSizes[i] = (int) size;
0817: }
0818:
0819: i++;
0820: }
0821:
0822: if (fillCnt > 0) {
0823: int fillSize = availableSize / fillCnt;
0824: int lastIndex = -1;
0825: i = 0;
0826:
0827: for (List o : sizes) {
0828: double size = o instanceof Row ? ((Row) o).getHeight()
0829: : ((Column) o).getWidth();
0830:
0831: if (size == 0) {
0832: absoluteSizes[i] = fillSize;
0833: lastIndex = i;
0834: }
0835:
0836: i++;
0837: }
0838:
0839: if (lastIndex != -1)
0840: absoluteSizes[lastIndex] += availableSize % fillCnt;
0841: }
0842:
0843: return absoluteSizes;
0844: }
0845:
0846: protected void update() {
0847: if (container == null)
0848: return;
0849:
0850: SortedSet<Row> visibleRows = getVisibleRows();
0851: SortedSet<Column> visibleColumns = getVisibleColumns();
0852: int[] absoluteWidths = getAbsoluteSizes(calculateInnerWidth(),
0853: visibleColumns);
0854: int[] absoluteHeights = getAbsoluteSizes(container
0855: .getInnerHeight(), visibleRows);
0856:
0857: Column[] columnArray = visibleColumns
0858: .toArray(new Column[visibleColumns.size()]);
0859: Map<Integer, List<Component>> rowComponents = new HashMap<Integer, List<Component>>();
0860: Map<Integer, List<Component>> colComponents = new HashMap<Integer, List<Component>>();
0861:
0862: int rowIndex = 0;
0863: for (Row r : visibleRows) {
0864: for (int i = 0, cnt = columnArray.length; i < cnt; i++) {
0865: Object o = r.get(columnArray[i].getIndex());
0866: if (o instanceof List) {
0867: List<Component> compList = (List<Component>) o;
0868: for (Component c : compList) {
0869: processComponent(c, rowComponents,
0870: colComponents, rowIndex, i,
0871: absoluteWidths, absoluteHeights);
0872: }
0873: } else if (o instanceof Component) {
0874: Component c = (Component) o;
0875: processComponent(c, rowComponents, colComponents,
0876: rowIndex, i, absoluteWidths,
0877: absoluteHeights);
0878: }
0879: }
0880:
0881: rowIndex++;
0882: }
0883: }
0884:
0885: private void processComponent(Component c,
0886: Map<Integer, List<Component>> rowComponents,
0887: Map<Integer, List<Component>> colComponents, int rowIndex,
0888: int i, int[] absoluteWidths, int[] absoluteHeights) {
0889: Range limit = (Range) c.getLimit();
0890:
0891: Justify just = limit.getJustify();
0892: int x = margin, y = margin, width = 0, height = 0;
0893:
0894: if (rowComponents.get(rowIndex) == null)
0895: rowComponents.put(rowIndex, new ArrayList<Component>());
0896: if (colComponents.get(i) == null)
0897: colComponents.put(i, new ArrayList<Component>());
0898:
0899: if (rowComponents.get(rowIndex).contains(c)) {
0900: width = c.getWidth();
0901: height = c.getHeight();
0902: x = c.getX();
0903: y = c.getY();
0904:
0905: if (!colComponents.get(i).contains(c)) {
0906: colComponents.get(i).add(c);
0907: width += spacing + absoluteWidths[i];
0908: }
0909: } else if (colComponents.get(i).contains(c)) {
0910: width = c.getWidth();
0911: height = c.getHeight();
0912: x = c.getX();
0913: y = c.getY();
0914:
0915: if (!rowComponents.get(rowIndex).contains(c)) {
0916: rowComponents.get(rowIndex).add(c);
0917: height += spacing + absoluteHeights[rowIndex];
0918: }
0919: } else {
0920: rowComponents.get(rowIndex).add(c);
0921: colComponents.get(i).add(c);
0922:
0923: for (int j = 0; j < i; j++) {
0924: x += absoluteWidths[j] + spacing;
0925: }
0926:
0927: for (int j = 0; j < rowIndex; j++) {
0928: y += absoluteHeights[j] + spacing;
0929: }
0930:
0931: width += absoluteWidths[i];
0932:
0933: if (just != Justify.FULL && c.getWidth() < width)
0934: width = c.getWidth();
0935:
0936: height += absoluteHeights[rowIndex];
0937:
0938: if (just != Justify.FULL && c.getHeight() < height)
0939: height = c.getHeight();
0940:
0941: if (width == c.getWidth()) {
0942: if (just.name().indexOf("RIGHT") > -1) {
0943: x += absoluteWidths[i] - width;
0944: } else if (just == Justify.CENTER
0945: || just == Justify.CENTER_BOTTOM
0946: || just == Justify.CENTER_FULL
0947: || just == Justify.CENTER_TOP) {
0948: x += (absoluteWidths[i] / 2) - (width / 2);
0949: }
0950: }
0951:
0952: if (height == c.getHeight()) {
0953: if (just.name().indexOf("BOTTOM") > -1) {
0954: y += absoluteHeights[rowIndex] - height;
0955: } else if (just == Justify.CENTER
0956: || just == Justify.FULL_CENTER
0957: || just == Justify.LEFT_CENTER
0958: || just == Justify.RIGHT_CENTER) {
0959: y += (absoluteHeights[rowIndex] / 2) - (height / 2);
0960: }
0961: }
0962: }
0963:
0964: if (width >= 0 && height >= 0)
0965: c.setBounds(x, y, width, height);
0966: }
0967:
0968: public List<Column> getColumns() {
0969: return grid.getColumns();
0970: }
0971:
0972: public List<Row> getRows() {
0973: return grid.getRows();
0974: }
0975:
0976: public SortedSet<Column> getVisibleColumns() {
0977: return roVisibleColumns;
0978: }
0979:
0980: public SortedSet<Row> getVisibleRows() {
0981: return roVisibleRows;
0982: }
0983:
0984: public String toString() {
0985: return "TableLayout@" + System.identityHashCode(this )
0986: + "{columns.size=" + grid.getColumns().size()
0987: + ",rows.size=" + grid.getRows().size() + "}";
0988: }
0989:
0990: private int calculateInnerWidth() {
0991: if (container.getScrollType() != Container.ScrollType.NONE) {
0992: int totalHeight = 0;
0993: SortedSet<Row> visibleRows = getVisibleRows();
0994: for (Row r : visibleRows) {
0995: double height = r.getHeight();
0996: if (height >= 1)
0997: totalHeight += height;
0998: }
0999: totalHeight += spacing * visibleRows.size();
1000: totalHeight += margin * 2;
1001: int innerHeight = container.getInnerHeight();
1002: int innerWidth = container.getInnerWidth();
1003: if (innerHeight < totalHeight)
1004: innerWidth -= 20;
1005: return innerWidth;
1006: } else {
1007: return container.getInnerWidth();
1008: }
1009: }
1010: }
|