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;
0029:
0030: import java.util.Collection;
0031: import java.util.Collections;
0032: import java.util.Comparator;
0033: import java.util.List;
0034: import java.util.SortedSet;
0035: import java.util.TreeSet;
0036: import java.util.logging.Logger;
0037:
0038: import thinwire.ui.AlignTextComponent.AlignX;
0039: import thinwire.ui.DropDownGridBox;
0040: import thinwire.ui.DropDownGridBox.DefaultView;
0041: import thinwire.ui.event.ActionEvent;
0042: import thinwire.ui.event.ActionListener;
0043: import thinwire.ui.event.ItemChangeEvent;
0044: import thinwire.ui.event.ItemChangeListener;
0045: import thinwire.ui.event.PropertyChangeEvent;
0046: import thinwire.ui.event.PropertyChangeListener;
0047: import thinwire.ui.event.ItemChangeEvent.Type;
0048: import thinwire.ui.style.*;
0049: import thinwire.util.ArrayGrid;
0050: import thinwire.util.Grid;
0051:
0052: /**
0053: * A GridBox is a screen component that can display multi-column rows or data.
0054: * <p>
0055: * <b>Example:</b> <br>
0056: * <img src="doc-files/GridBox-1.png"> <br>
0057: *
0058: * <pre>
0059: * Dialog dlg = new Dialog("GridBox Test");
0060: * dlg.setBounds(25, 25, 600, 300);
0061: *
0062: * final TextField tf = new TextField();
0063: * tf.setBounds(375, 25, 150, 20);
0064: * dlg.getChildren().add(tf);
0065: *
0066: * GridBox gbx = new GridBox();
0067: * gbx.setBounds(25, 25, 300, 120);
0068: * gbx.setVisibleHeader(true);
0069: * gbx.setVisibleCheckBoxes(true);
0070: * gbx.setFullRowCheckBox(true);
0071: *
0072: * GridBox.Column col1 = new GridBox.Column();
0073: * col1.setName("Name");
0074: * col1.setVisible(true);
0075: * GridBox.Column col2 = new GridBox.Column();
0076: * col2.setName("City");
0077: * col2.setVisible(true);
0078: *
0079: * gbx.getColumns().add(col1);
0080: * gbx.getColumns().add(col2);
0081: * String[] names = { "Smythe", "Janes", "Warren", "Dempster", "Hilcox" };
0082: * String[] cities = { "Tokyo", "Hong Kong", "Lethbridge", "Moose Jaw", "Red Deer" };
0083: *
0084: * for (int r = 0; r < 5; r++) {
0085: * GridBox.Row row = new GridBox.Row();
0086: * row.add(names[r]);
0087: * row.add(cities[r]);
0088: * gbx.getRows().add(row);
0089: * }
0090: * gbx.addPropertyChangeListener(GridBox.Row.PROPERTY_ROW_SELECTED,
0091: * new PropertyChangeListener() {
0092: * public void propertyChange(PropertyChangeEvent evt) {
0093: * tf.setText((String) ((GridBox.Row) evt.getSource()).get(0));
0094: * }
0095: * });
0096: *
0097: * dlg.getChildren().add(gbx);
0098: * dlg.setVisible(true);
0099: * </pre>
0100: *
0101: * </p>
0102: * <p>
0103: * <b>Keyboard Navigation:</b><br>
0104: * <table border="1">
0105: * <tr>
0106: * <td>KEY</td>
0107: * <td>RESPONSE</td>
0108: * <td>NOTE</td>
0109: * </tr>
0110: * <tr>
0111: * <td>Space</td>
0112: * <td>Fires PropertyChangeEvent( propertyName = GridBox.Row.PROPERTY_CHECKED )</td>
0113: * <td>Only if isVisibleCheckBoxes() is true.</td>
0114: * </tr>
0115: * <tr>
0116: * <td>Enter</td>
0117: * <td>Fires Action( actionName = GridBox.ACTION_CLICK )</td>
0118: * <td></td>
0119: * </tr>
0120: * <tr>
0121: * <td>Arrow Up/Down</td>
0122: * <td>Fires PropertyChangeEvent( propertyName = GridBox.Row.PROPERTY_SELECTED )</td>
0123: * <td></td>
0124: * </tr>
0125: * </table>
0126: * </p>
0127: *
0128: * @author Joshua J. Gertzen
0129: */
0130: public class GridBox extends AbstractComponent implements
0131: Grid<GridBox.Row, GridBox.Column>, ItemChangeEventComponent {
0132: private static Logger log = Logger.getLogger(GridBox.class
0133: .getName());
0134:
0135: public static final class Range {
0136: private String stringValue;
0137: private GridBox gridBox;
0138: private int rowIndex;
0139: private int columnIndex;
0140:
0141: public Range(GridBox gridBox, int columnIndex, int rowIndex) {
0142: if (gridBox == null)
0143: throw new IllegalArgumentException("gridBox == null");
0144: if (columnIndex < -1)
0145: throw new IllegalArgumentException("columnIndex{"
0146: + columnIndex + "} < -1");
0147: if (rowIndex < -1)
0148: throw new IllegalArgumentException("rowIndex{"
0149: + rowIndex + "} < -1");
0150: this .gridBox = gridBox;
0151: this .rowIndex = rowIndex;
0152: this .columnIndex = columnIndex;
0153: }
0154:
0155: public GridBox getParent() {
0156: return gridBox;
0157: }
0158:
0159: public Column getColumn() {
0160: if (columnIndex < 0
0161: || columnIndex >= gridBox.getColumns().size())
0162: return null;
0163: return gridBox.getColumns().get(columnIndex);
0164: }
0165:
0166: public int getColumnIndex() {
0167: return columnIndex;
0168: }
0169:
0170: public Row getRow() {
0171: if (rowIndex < 0 || rowIndex >= gridBox.getRows().size())
0172: return null;
0173: return gridBox.getRows().get(rowIndex);
0174: }
0175:
0176: public int getRowIndex() {
0177: return rowIndex;
0178: }
0179:
0180: public Object getCell() {
0181: Column col = getColumn();
0182: if (col == null || rowIndex < 0
0183: || rowIndex >= gridBox.getRows().size())
0184: throw new IllegalStateException(
0185: "columnIndex < 0 || columnIndex >= getColumns().size() || rowIndex < 0 || rowIndex >= getRows().size()");
0186: return col.get(rowIndex);
0187: }
0188:
0189: public void setCell(Object value) {
0190: Column col = getColumn();
0191: if (col == null || rowIndex < 0
0192: || rowIndex >= gridBox.getRows().size())
0193: throw new IllegalStateException(
0194: "columnIndex < 0 || columnIndex >= getColumns().size() || rowIndex < 0 || rowIndex >= getRows().size()");
0195: col.set(rowIndex, value);
0196: }
0197:
0198: public boolean equals(Object o) {
0199: return o instanceof Range
0200: && toString().equals(o.toString());
0201: }
0202:
0203: public int hashCode() {
0204: return toString().hashCode();
0205: }
0206:
0207: public String toString() {
0208: if (stringValue == null)
0209: stringValue = "GridBox.Range{columnIndex:"
0210: + columnIndex + ",rowIndex:" + rowIndex + "}";
0211: return stringValue;
0212: }
0213: }
0214:
0215: public static final class Row extends ArrayGrid.Row {
0216: public static final String PROPERTY_ROW_SELECTED = "rowSelected";
0217: public static final String PROPERTY_ROW_CHECKED = "rowChecked";
0218: public static final String PROPERTY_ROW_CHILD = "rowChild";
0219:
0220: private GridBox child;
0221: private boolean checked;
0222: private boolean selected;
0223:
0224: /**
0225: * Construct a Row.
0226: */
0227: public Row() {
0228:
0229: }
0230:
0231: /**
0232: * Construct a Row that contains the values of the specified Collection.
0233: * @param c the new Row's values.
0234: */
0235: public Row(Collection<? extends Object> c) {
0236: this (c.toArray(), false);
0237: }
0238:
0239: /**
0240: * Construct a Row that contains the values of the specified Collection.
0241: * @param a the new Row's values.
0242: */
0243: public Row(boolean checked, Collection<? extends Object> c) {
0244: this (c.toArray(), checked);
0245: }
0246:
0247: /**
0248: * Construct a Row that contains the values of the specified Array.
0249: * @param a the new Row's values.
0250: */
0251: public Row(Object... a) {
0252: this (a, false);
0253: }
0254:
0255: /**
0256: * Construct a Row that contains the values of the specified Array.
0257: * @param a the new Row's values.
0258: */
0259: public Row(boolean checked, Object... a) {
0260: this (a, checked);
0261: }
0262:
0263: private Row(Object[] a, boolean checked) {
0264: this ();
0265:
0266: if (selected)
0267: setSelected(true);
0268: if (checked)
0269: setChecked(true);
0270:
0271: for (Object o : a)
0272: add(o);
0273: }
0274:
0275: /**
0276: * Returns the checked state of the row.
0277: * @return the checked state of the row.
0278: * @throws IllegalStateException if the row has not been added to a GridBox.
0279: */
0280: public boolean isChecked() {
0281: return checked;
0282: }
0283:
0284: /**
0285: * Sets the checked state of the row.
0286: * @param checked true to check the row, false to uncheck it.
0287: */
0288: public void setChecked(boolean checked) {
0289: if (this .checked == checked)
0290: return;
0291: GridBox gb = (GridBox) getParent();
0292:
0293: if (gb == null) {
0294: this .checked = checked;
0295: } else {
0296: boolean oldChecked = this .checked;
0297: if (oldChecked)
0298: gb.checkedRows.remove(this );
0299: this .checked = checked;
0300: if (checked)
0301: gb.checkedRows.add(this );
0302:
0303: if (gb.firePropertyChange(this , PROPERTY_ROW_CHECKED,
0304: oldChecked, checked)) {
0305: }
0306: }
0307: }
0308:
0309: /**
0310: * Returns the selected state of the row.
0311: * @return the selected state of the row.
0312: */
0313: public boolean isSelected() {
0314: return selected;
0315: }
0316:
0317: /**
0318: * Sets the selected state of the row.
0319: * @param selected true to select the row, false to unselect it.
0320: */
0321: public void setSelected(boolean selected) {
0322: this .selected = selected;
0323: GridBox gb = (GridBox) getParent();
0324:
0325: if (gb != null && !gb.sorting) {
0326: if (selected && gb.selectedRow != this ) {
0327: if (gb.selectedRow != null) {
0328: gb.firePropertyChange(gb.selectedRow,
0329: PROPERTY_ROW_SELECTED, true, false);
0330: gb.selectedRow.selected = false;
0331: }
0332:
0333: gb.priorSelectedRow = gb.selectedRow;
0334: gb.selectedRow = this ;
0335: gb.firePropertyChange(this , PROPERTY_ROW_SELECTED,
0336: false, true);
0337: } else if (!selected && gb.selectedRow == this ) {
0338: gb.firePropertyChange(this , PROPERTY_ROW_SELECTED,
0339: true, false);
0340: gb.priorSelectedRow = this ;
0341: gb.selectedRow = gb.getRows().size() > 0 ? gb
0342: .getRows().get(0) : null;
0343: if (gb.selectedRow != null)
0344: gb.firePropertyChange(gb.selectedRow,
0345: PROPERTY_ROW_SELECTED, false, true);
0346: }
0347: }
0348: }
0349:
0350: /**
0351: * Get this Row's child GridBox.
0352: * @return this Row's child GridBox.
0353: */
0354: public GridBox getChild() {
0355: return child;
0356: }
0357:
0358: /**
0359: * Set this Row's child GridBox.
0360: * @param child the child GridBox
0361: */
0362: public void setChild(GridBox child) {
0363: GridBox gb = (GridBox) getParent();
0364: if (gb != null && gb.isVisibleCheckBoxes())
0365: throw new IllegalStateException(
0366: "getParent().isVisibleCheckBoxes() == true");
0367: if (child != null && child.isVisibleCheckBoxes())
0368: throw new IllegalStateException(
0369: "child.isVisibleCheckBoxes() == true");
0370: GridBox oldChild = this .child;
0371: this .child = child;
0372: if (child != null)
0373: child.setParent(this );
0374:
0375: if (gb != null) {
0376: if (oldChild != null)
0377: gb.rowsWithChildren.remove(this );
0378:
0379: if (child != null) {
0380: gb.rowsWithChildren.add(this );
0381: DropDown.copyDropDownStyle(gb, child, true);
0382: }
0383:
0384: gb.firePropertyChange(this , PROPERTY_ROW_CHILD,
0385: oldChild, child);
0386: }
0387: }
0388:
0389: public String toString() {
0390: GridBox gb = (GridBox) getParent();
0391: return "GridBox.Row@"
0392: + System.identityHashCode(this )
0393: + "{index:"
0394: + getIndex()
0395: + ",selected:"
0396: + isSelected()
0397: + ",checked:"
0398: + isChecked()
0399: + ",size():"
0400: + size()
0401: + ",parent:"
0402: + (gb == null ? null : "GridBox@"
0403: + System.identityHashCode(gb))
0404: + ",child:"
0405: + (child == null ? null : "GridBox@"
0406: + System.identityHashCode(child)) + "}";
0407: }
0408: }
0409:
0410: public static final class Column extends ArrayGrid.Column {
0411: public static final String PROPERTY_COLUMN_NAME = "columnName";
0412: public static final String PROPERTY_COLUMN_ALIGN_X = "columnAlignX";
0413: public static final String PROPERTY_COLUMN_WIDTH = "columnWidth";
0414: public static final String PROPERTY_COLUMN_VISIBLE = "columnVisible";
0415: public static final String PROPERTY_COLUMN_DISPLAY_FORMAT = "columnDisplayFormat";
0416: public static final String PROPERTY_COLUMN_SORT_COMPARATOR = "columnSortComparator";
0417: public static final String PROPERTY_COLUMN_SORT_ORDER = "columnSortOrder";
0418:
0419: public static enum SortOrder {
0420: NONE, ASC, DESC
0421: };
0422:
0423: public interface Format {
0424: public Object format(Object value);
0425: }
0426:
0427: private static final Comparator DEFAULT_COMPARATOR = new Comparator() {
0428: public int compare(Object o1, Object o2) {
0429: if (o1 == null)
0430: o1 = "";
0431: if (o2 == null)
0432: o2 = "";
0433: String s1 = o1 instanceof String ? (String) o1 : o1
0434: .toString();
0435: String s2 = o2 instanceof String ? (String) o2 : o2
0436: .toString();
0437: return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
0438: }
0439: };
0440:
0441: //Visible used to default to false, which creates confusion when using this component.
0442: private Header header = new Header(this );
0443: private boolean visible = true;
0444: private int width = -1;
0445: private AlignX alignX = AlignX.LEFT;
0446: private Format displayFormat;
0447: private Comparator sortComparator = DEFAULT_COMPARATOR;
0448:
0449: /**
0450: * Construct a Column.
0451: */
0452: public Column() {
0453:
0454: }
0455:
0456: /**
0457: * Construct a Column, specifying a Collection whose values will serve as the Column's values.
0458: * @param c the new Column's values
0459: */
0460: public Column(Collection<? extends Object> c) {
0461: this (c.toArray(), null, true, -1, null);
0462: }
0463:
0464: /**
0465: * Construct a Column that contains the values of the specified Array.
0466: * @param a the new Column's values
0467: */
0468: public Column(String name, boolean visible,
0469: Collection<? extends Object> c) {
0470: this (c.toArray(), name, visible, -1, null);
0471: }
0472:
0473: /**
0474: * Construct a Column that contains the values of the specified Array.
0475: * @param a the new Column's values
0476: */
0477: public Column(String name, boolean visible, int width,
0478: Collection<? extends Object> c) {
0479: this (c.toArray(), name, visible, width, null);
0480: }
0481:
0482: /**
0483: * Construct a Column that contains the values of the specified Array.
0484: * @param a the new Column's values
0485: */
0486: public Column(String name, boolean visible, int width,
0487: AlignX alignX, Collection<? extends Object> c) {
0488: this (c.toArray(), name, visible, width, alignX);
0489: }
0490:
0491: /**
0492: * Construct a Column that contains the values of the specified Array.
0493: * @param a the new Column's values
0494: */
0495: public Column(Object... a) {
0496: this (a, null, true, -1, null);
0497: }
0498:
0499: /**
0500: * Construct a Column that contains the values of the specified Array.
0501: * @param a the new Column's values
0502: */
0503: public Column(String name, boolean visible, Object... a) {
0504: this (a, name, visible, -1, null);
0505: }
0506:
0507: /**
0508: * Construct a Column that contains the values of the specified Array.
0509: * @param a the new Column's values
0510: */
0511: public Column(String name, boolean visible, int width,
0512: Object... a) {
0513: this (a, name, visible, width, null);
0514: }
0515:
0516: /**
0517: * Construct a Column that contains the values of the specified Array.
0518: * @param a the new Column's values
0519: */
0520: public Column(String name, boolean visible, int width,
0521: AlignX alignX, Object... a) {
0522: this (a, name, visible, width, alignX);
0523: }
0524:
0525: private Column(Object[] values, String name, boolean visible,
0526: int width, AlignX alignX) {
0527: this ();
0528: if (name != null)
0529: setName(name);
0530: if (visible != this .visible)
0531: setVisible(visible);
0532: if (width != -1)
0533: setWidth(width);
0534: if (alignX != null)
0535: setAlignX(alignX);
0536:
0537: for (Object o : values)
0538: add(o);
0539: }
0540:
0541: /**
0542: * Gets the Header object instance for this Column.
0543: * @return the Header object representing this Column.
0544: */
0545: public Header getHeader() {
0546: return header;
0547: }
0548:
0549: /**
0550: * Gets whether this column is visible.
0551: * @return true if the column is visible.
0552: */
0553: public boolean isVisible() {
0554: return visible;
0555: }
0556:
0557: /**
0558: * Sets whether this column is visible.
0559: * @param visible (Default = false)
0560: */
0561: public void setVisible(boolean visible) {
0562: boolean oldVisible = this .visible;
0563: GridBox gb = (GridBox) getParent();
0564: this .visible = visible;
0565: if (gb != null)
0566: gb.firePropertyChange(this , PROPERTY_COLUMN_VISIBLE,
0567: oldVisible, visible);
0568: }
0569:
0570: /**
0571: * Sets the name of the column.
0572: */
0573: public void setName(String name) {
0574: String oldName = getName();
0575: name = name == null ? "" : name;
0576: GridBox gb = (GridBox) getParent();
0577: super .setName(name);
0578: if (gb != null)
0579: gb.firePropertyChange(this , PROPERTY_COLUMN_NAME,
0580: oldName, name);
0581: }
0582:
0583: public int getWidth() {
0584: return width;
0585: }
0586:
0587: /**
0588: * Sets the width of the column.
0589: * @param width the width of the column
0590: * @throws IllegalArgumentException if width < -1 or > 65534
0591: */
0592: public void setWidth(int width) {
0593: if (width < -1 || width >= 65535)
0594: throw new IllegalArgumentException(
0595: "width < -1 || width >= 65535");
0596: int oldWidth = this .width;
0597: GridBox gb = (GridBox) getParent();
0598: this .width = width;
0599: if (gb != null)
0600: gb.firePropertyChange(this , PROPERTY_COLUMN_WIDTH,
0601: oldWidth, width);
0602: }
0603:
0604: public AlignX getAlignX() {
0605: return alignX;
0606: }
0607:
0608: /**
0609: * Sets the text justification (left, right, or center)
0610: * @param alignX (Default = AlignX.LEFT)
0611: */
0612: public void setAlignX(AlignX alignX) {
0613: if (alignX == null)
0614: alignX = AlignX.LEFT;
0615: AlignX oldAlignX = this .alignX;
0616: GridBox gb = (GridBox) getParent();
0617: this .alignX = alignX;
0618: if (gb != null)
0619: gb.firePropertyChange(this , PROPERTY_COLUMN_ALIGN_X,
0620: oldAlignX, alignX);
0621: }
0622:
0623: public Format getDisplayFormat() {
0624: return displayFormat;
0625: }
0626:
0627: /**
0628: * Set the display format for this Column.
0629: * A Column's Displ
0630: * @param displayFormat the display format to set for the column.
0631: */
0632: public void setDisplayFormat(Format displayFormat) {
0633: Format oldDisplayFormat = this .displayFormat;
0634: GridBox gb = (GridBox) getParent();
0635: this .displayFormat = displayFormat;
0636: if (gb != null)
0637: gb.firePropertyChange(this ,
0638: PROPERTY_COLUMN_DISPLAY_FORMAT,
0639: oldDisplayFormat, displayFormat);
0640: }
0641:
0642: /**
0643: * Get this Column's Comparator.
0644: * @return this Column's Comparator
0645: */
0646: public Comparator getSortComparator() {
0647: return sortComparator;
0648: }
0649:
0650: /**
0651: * Set the Comparator for this Column.
0652: * @see java.util.Comparator
0653: * @param sortComparator this Column's Comparator.
0654: */
0655: public void setSortComparator(Comparator sortComparator) {
0656: if (sortComparator == null)
0657: sortComparator = DEFAULT_COMPARATOR;
0658: Comparator oldSortComparator = this .sortComparator;
0659: GridBox gb = (GridBox) getParent();
0660: this .sortComparator = sortComparator;
0661: if (gb != null)
0662: gb.firePropertyChange(this ,
0663: PROPERTY_COLUMN_SORT_COMPARATOR,
0664: oldSortComparator, sortComparator);
0665: setSortOrder(null);
0666: }
0667:
0668: public SortOrder getSortOrder() {
0669: GridBox gb = (GridBox) getParent();
0670: return gb == null || gb.sortedColumn != this ? SortOrder.NONE
0671: : gb.sortedColumnOrder;
0672: }
0673:
0674: public void setSortOrder(SortOrder sortOrder) {
0675: GridBox gb = (GridBox) getParent();
0676: if (gb == null)
0677: throw new IllegalStateException(
0678: "the column must be added to a GridBox before you can sort by it");
0679: if (sortOrder == null)
0680: sortOrder = SortOrder.NONE;
0681: GridBox.Row selectedRow = gb.getSelectedRow();
0682:
0683: GridBox.Column oldSortedColumn = gb.sortedColumn;
0684: SortOrder oldSortedColumnOrder = gb.sortedColumnOrder;
0685: gb.sortedColumn = oldSortedColumn == this
0686: && sortOrder == SortOrder.NONE ? null : this ;
0687: gb.sortedColumnOrder = sortOrder;
0688:
0689: if (oldSortedColumn != null && oldSortedColumn != this )
0690: gb.firePropertyChange(oldSortedColumn,
0691: PROPERTY_COLUMN_SORT_ORDER,
0692: oldSortedColumnOrder, SortOrder.NONE);
0693: gb.firePropertyChange(this , PROPERTY_COLUMN_SORT_ORDER,
0694: oldSortedColumn == this ? oldSortedColumnOrder
0695: : SortOrder.NONE, sortOrder);
0696:
0697: gb.sort();
0698: if (selectedRow != null)
0699: selectedRow.setSelected(true);
0700: }
0701:
0702: public String toString() {
0703: GridBox gb = (GridBox) getParent();
0704: return "GridBox.Column@"
0705: + System.identityHashCode(this )
0706: + "{index:"
0707: + getIndex()
0708: + ",name:"
0709: + getName()
0710: + ",header:"
0711: + getHeader()
0712: + ",visible:"
0713: + isVisible()
0714: + ",width:"
0715: + getWidth()
0716: + ",alignX:"
0717: + getAlignX()
0718: + ",sortOrder:"
0719: + getSortOrder()
0720: + ",sortComparator:"
0721: + getSortComparator()
0722: + ",size():"
0723: + size()
0724: + ",parent:"
0725: + (gb == null ? null : "GridBox@"
0726: + System.identityHashCode(gb)) + "}";
0727: }
0728: }
0729:
0730: public static final class Header {
0731: public static final String PROPERTY_HEADER_TEXT = "headerText";
0732: private Column column;
0733: private String text = "";
0734:
0735: private Header(Column column) {
0736: this .column = column;
0737: }
0738:
0739: /**
0740: * Returns the text to display in column headers.
0741: * If this property has been set, it is returned.
0742: * Otherwise the column's name property is returned.
0743: * @return Returns the displayName.
0744: */
0745: public String getText() {
0746: String name = column.getName();
0747:
0748: if (this .text.length() > 0) {
0749: return this .text;
0750: } else if (name != null && name.length() > 0) {
0751: return name;
0752: } else {
0753: return "";
0754: }
0755: }
0756:
0757: /**
0758: * Sets the text to display in the header of the column.
0759: */
0760: public void setText(String text) {
0761: String oldText = getText();
0762: text = text == null ? "" : text;
0763: GridBox gb = (GridBox) column.getParent();
0764: this .text = text;
0765: if (gb != null)
0766: gb.firePropertyChange(this , PROPERTY_HEADER_TEXT,
0767: oldText, text);
0768: }
0769:
0770: public String toString() {
0771: return "Header{text:" + getText() + "}";
0772: }
0773: }
0774:
0775: public static final String PROPERTY_VISIBLE_HEADER = "visibleHeader";
0776: public static final String PROPERTY_VISIBLE_CHECK_BOXES = "visibleCheckBoxes";
0777: public static final String PROPERTY_FULL_ROW_CHECK_BOX = "fullRowCheckBox";
0778: public static final String PROPERTY_SORT_ALLOWED = "sortAllowed";
0779:
0780: private static final String[] STYLE_PROPERTIES = {
0781: Font.PROPERTY_FONT_BOLD, Font.PROPERTY_FONT_COLOR,
0782: Font.PROPERTY_FONT_FAMILY, Font.PROPERTY_FONT_ITALIC,
0783: Font.PROPERTY_FONT_SIZE, Font.PROPERTY_FONT_UNDERLINE,
0784: Font.PROPERTY_FONT_STRIKE,
0785: Background.PROPERTY_BACKGROUND_COLOR,
0786: Background.PROPERTY_BACKGROUND_IMAGE,
0787: Background.PROPERTY_BACKGROUND_POSITION,
0788: Background.PROPERTY_BACKGROUND_REPEAT,
0789: Border.PROPERTY_BORDER_COLOR, Border.PROPERTY_BORDER_IMAGE,
0790: Border.PROPERTY_BORDER_SIZE, Border.PROPERTY_BORDER_TYPE };
0791:
0792: private boolean visibleHeader;
0793: private boolean visibleCheckBoxes;
0794: private boolean fullRowCheckBox;
0795: private boolean sortAllowed = true;
0796: private Row selectedRow;
0797: private Row priorSelectedRow;
0798: private Column sortedColumn;
0799: private Column.SortOrder sortedColumnOrder = GridBox.Column.SortOrder.NONE;
0800:
0801: private EventListenerImpl<ItemChangeListener> icei;
0802: private ArrayGrid<Row, Column> grid;
0803: private SortedSet<Row> checkedRows;
0804: private SortedSet<Row> roCheckedRows;
0805: private SortedSet<Row> rowsWithChildren;
0806: private SortedSet<Row> roRowsWithChildren;
0807:
0808: boolean sorting = false;
0809:
0810: /**
0811: * Constructs a new GridBox.
0812: */
0813: public GridBox() {
0814: EventListenerImpl<ItemChangeListener> gicei = app == null ? null
0815: : app.getGlobalListenerSet(ItemChangeListener.class,
0816: false);
0817: icei = new EventListenerImpl<ItemChangeListener>(this ,
0818: ItemChangeListener.class, null, gicei);
0819:
0820: this .grid = new ArrayGrid<GridBox.Row, GridBox.Column>(this ,
0821: GridBox.Row.class, GridBox.Column.class) {
0822: protected void fireItemChange(Type type, int rowIndex,
0823: int columnIndex, Object oldValue, Object newValue) {
0824: if (rowIndex >= 0 && columnIndex == -1) {
0825: List<GridBox.Row> rows = GridBox.this .getRows();
0826: int size = rows.size();
0827:
0828: //If the selected row is removed or if the first row is added, then
0829: //we need to guarantee the selected row is correct.
0830: if (type == ItemChangeEvent.Type.REMOVE) {
0831: if (getRows().size() == 0) { //Clear was called
0832: GridBox.this .rowsWithChildren.clear();
0833: GridBox.this .checkedRows.clear();
0834: GridBox.this .selectedRow = null;
0835: GridBox.this .priorSelectedRow = null;
0836: } else {
0837: GridBox.Row oldRow = (GridBox.Row) oldValue;
0838: if (oldRow.getChild() != null)
0839: GridBox.this .rowsWithChildren
0840: .remove(oldRow);
0841: if (oldRow.isChecked())
0842: GridBox.this .checkedRows.remove(oldRow);
0843:
0844: if (oldRow == GridBox.this .selectedRow) {
0845: if (rowIndex < size) {
0846: rows.get(rowIndex)
0847: .setSelected(true);
0848: } else if (size > 0) {
0849: rows.get(size - 1)
0850: .setSelected(true);
0851: } else {
0852: GridBox.this .selectedRow = null;
0853: }
0854: }
0855:
0856: if (oldRow == GridBox.this .priorSelectedRow)
0857: GridBox.this .priorSelectedRow = null;
0858: }
0859: } else if (type == ItemChangeEvent.Type.ADD) {
0860: GridBox.Row newRow = (GridBox.Row) newValue;
0861:
0862: if (GridBox.this .visibleCheckBoxes
0863: && newRow.getChild() != null)
0864: throw new IllegalStateException(
0865: "GridBox.this.visibleCheckBoxes && newRow.getChild() != null");
0866:
0867: if (newRow.getChild() != null) {
0868: GridBox.this .rowsWithChildren.add(newRow);
0869: DropDown.copyDropDownStyle(GridBox.this ,
0870: newRow.getChild(),
0871: getParent() instanceof DropDown);
0872: }
0873:
0874: if (newRow.isChecked())
0875: GridBox.this .checkedRows.add(newRow);
0876:
0877: if (size == 1 || newRow.isSelected())
0878: newRow.setSelected(true);
0879:
0880: if (GridBox.this .sortedColumn != null)
0881: GridBox.this .sortedColumn
0882: .setSortOrder(GridBox.Column.SortOrder.NONE);
0883: } else if (type == ItemChangeEvent.Type.SET) {
0884: GridBox.Row newRow = (GridBox.Row) newValue;
0885: GridBox.Row oldRow = (GridBox.Row) oldValue;
0886:
0887: if (GridBox.this .visibleCheckBoxes
0888: && newRow.getChild() != null)
0889: throw new IllegalStateException(
0890: "GridBox.this.visibleCheckBoxes && newRow.getChild() != null");
0891:
0892: if (oldRow.getChild() != null)
0893: GridBox.this .rowsWithChildren
0894: .remove(oldRow);
0895: if (oldRow.isChecked())
0896: GridBox.this .checkedRows.remove(oldRow);
0897:
0898: if (newRow.getChild() != null) {
0899: GridBox.this .rowsWithChildren.add(newRow);
0900: DropDown.copyDropDownStyle(GridBox.this ,
0901: newRow.getChild(),
0902: getParent() instanceof DropDown);
0903: }
0904:
0905: if (newRow.isChecked())
0906: GridBox.this .checkedRows.add(newRow);
0907: if (oldRow.isSelected())
0908: newRow.setSelected(true);
0909: if (oldRow == GridBox.this .priorSelectedRow)
0910: GridBox.this .priorSelectedRow = null;
0911: }
0912: }
0913:
0914: icei.fireItemChange(type, columnIndex, rowIndex,
0915: oldValue, newValue);
0916: }
0917: };
0918:
0919: Comparator<Row> indexOrder = new Comparator<Row>() {
0920: public int compare(Row r1, Row r2) {
0921: int index1 = r1.getIndex();
0922: int index2 = r2.getIndex();
0923:
0924: if (index1 < index2) {
0925: return -1;
0926: } else if (index1 == index2) {
0927: return 0;
0928: } else {
0929: return 1;
0930: }
0931: }
0932: };
0933:
0934: addPropertyChangeListener(STYLE_PROPERTIES,
0935: new PropertyChangeListener() {
0936: public void propertyChange(PropertyChangeEvent ev) {
0937: String propertyName = ev.getPropertyName();
0938: Object o = ev.getNewValue();
0939:
0940: for (GridBox.Row row : getRowsWithChildren()) {
0941: Style s = row.getChild().getStyle();
0942: s.setProperty(propertyName, o);
0943: }
0944: }
0945: });
0946:
0947: checkedRows = new TreeSet<Row>(indexOrder);
0948: roCheckedRows = Collections.unmodifiableSortedSet(checkedRows);
0949: rowsWithChildren = new TreeSet<Row>(indexOrder);
0950: roRowsWithChildren = Collections
0951: .unmodifiableSortedSet(rowsWithChildren);
0952: }
0953:
0954: private void sort() {
0955: if (sortedColumnOrder == GridBox.Column.SortOrder.NONE
0956: || sortedColumn == null)
0957: return;
0958: sorting = true;
0959: final int index = sortedColumn.getIndex();
0960: final Comparator<Object> sortComparator = sortedColumn
0961: .getSortComparator();
0962:
0963: if (sortedColumnOrder == GridBox.Column.SortOrder.DESC) {
0964: Collections.sort(getRows(), new Comparator<Row>() {
0965: public int compare(Row o1, Row o2) {
0966: return ~sortComparator.compare(o1.get(index), o2
0967: .get(index)) + 1;
0968: }
0969: });
0970: } else {
0971: Collections.sort(getRows(), new Comparator<Row>() {
0972: public int compare(Row o1, Row o2) {
0973: return sortComparator.compare(o1.get(index), o2
0974: .get(index));
0975: }
0976: });
0977: }
0978:
0979: sorting = false;
0980: }
0981:
0982: public void addItemChangeListener(ItemChangeListener listener) {
0983: icei.addListener(listener);
0984: }
0985:
0986: public void removeItemChangeListener(ItemChangeListener listener) {
0987: icei.removeListener(listener);
0988: }
0989:
0990: public void fireAction(ActionEvent ev) {
0991: if (ev == null)
0992: throw new IllegalArgumentException("ev == null");
0993: if (!(ev.getSource() instanceof Range))
0994: throw new IllegalArgumentException(
0995: "!(ev.getSource() instanceof GridBox.Range)");
0996: Row row = ((Range) ev.getSource()).getRow();
0997: if (row != null)
0998: row.setSelected(true);
0999: super .fireAction(ev);
1000: }
1001:
1002: public List<GridBox.Column> getColumns() {
1003: return grid.getColumns();
1004: }
1005:
1006: public List<GridBox.Row> getRows() {
1007: return grid.getRows();
1008: }
1009:
1010: /**
1011: * Returns a boolean indicating if this GridBox's column headers are
1012: * visible.
1013: *
1014: * @return true if this GridBox's column headers are visible.
1015: */
1016: public boolean isVisibleHeader() {
1017: return visibleHeader;
1018: }
1019:
1020: /**
1021: * Sets whether the column headers are visible.
1022: * @param visibleHeader Default: false
1023: */
1024: public void setVisibleHeader(boolean visibleHeader) {
1025: boolean oldVisibleHeader = this .visibleHeader;
1026: this .visibleHeader = visibleHeader;
1027: firePropertyChange(this , PROPERTY_VISIBLE_HEADER,
1028: oldVisibleHeader, visibleHeader);
1029: }
1030:
1031: /**
1032: * Returns a boolean indicating whether this GridBox's rows have visible
1033: * CheckBoxes.
1034: *
1035: * @return true if there are visible CheckBoxes on the rows.
1036: */
1037: public boolean isVisibleCheckBoxes() {
1038: return visibleCheckBoxes;
1039: }
1040:
1041: /**
1042: * Sets whether there are checkBoxes on each row.
1043: * @param visibleCheckBoxes Default: false
1044: */
1045: public void setVisibleCheckBoxes(boolean visibleCheckBoxes) {
1046: if (visibleCheckBoxes && !this .visibleCheckBoxes
1047: && rowsWithChildren.size() > 0)
1048: throw new IllegalStateException(
1049: "getRowsWithChildren().size() > 0 ["
1050: + rowsWithChildren.size() + "]");
1051: if (getParent() instanceof GridBox.Row)
1052: throw new IllegalStateException(
1053: "getParent() instanceof GridBox.Row");
1054: boolean oldVisibleCheckBoxes = this .visibleCheckBoxes;
1055: this .visibleCheckBoxes = visibleCheckBoxes;
1056: firePropertyChange(this , PROPERTY_VISIBLE_CHECK_BOXES,
1057: oldVisibleCheckBoxes, visibleCheckBoxes);
1058: }
1059:
1060: /**
1061: * Get a boolean indicating whether clicking anywhere on a row in this
1062: * GridBox will check the row's CheckBox.
1063: *
1064: * @return true if clicking anywhere on this GridBox's rows will check the
1065: * row's CheckBox.
1066: */
1067: public boolean isFullRowCheckBox() {
1068: return fullRowCheckBox;
1069: }
1070:
1071: /**
1072: * When check boxes are turned on, if this flag is true, clicking anywhere on the row will
1073: * check the box.
1074: * @param fullRowCheckBox boolean which turns on CheckBox checking.
1075: */
1076: public void setFullRowCheckBox(boolean fullRowCheckBox) {
1077: boolean oldFullRowCheckBox = this .fullRowCheckBox;
1078: this .fullRowCheckBox = fullRowCheckBox;
1079: firePropertyChange(this , PROPERTY_FULL_ROW_CHECK_BOX,
1080: oldFullRowCheckBox, fullRowCheckBox);
1081: }
1082:
1083: /**
1084: * Get a boolean indicating whether this GridBox can be sorted by clicking
1085: * on Column headers
1086: *
1087: * @return true if the GridBox can be sorted
1088: */
1089: public boolean isSortAllowed() {
1090: return sortAllowed;
1091: }
1092:
1093: /**
1094: * Sets whether the GridBox can be sorted by clicking on Column headers.
1095: *
1096: * @param sortAllowed Default: true
1097: */
1098: public void setSortAllowed(boolean sortAllowed) {
1099: boolean oldSortAllowed = this .sortAllowed;
1100: this .sortAllowed = sortAllowed;
1101: firePropertyChange(this , PROPERTY_SORT_ALLOWED, oldSortAllowed,
1102: sortAllowed);
1103: }
1104:
1105: /**
1106: * Returns the row that is currently selected.
1107: * @return the selected Row.
1108: */
1109: public Row getSelectedRow() {
1110: return selectedRow;
1111: }
1112:
1113: /**
1114: * Returns the prior row that was selected.
1115: * @return the prior row that was selected.
1116: */
1117: public Row getPriorSelectedRow() {
1118: return priorSelectedRow;
1119: }
1120:
1121: /**
1122: * Returns any rows that are checked.
1123: * @return an unmodifiableSortedSet of checked rows in index top-down order.
1124: */
1125: public SortedSet<Row> getCheckedRows() {
1126: return roCheckedRows;
1127: }
1128:
1129: /**
1130: * Returns any rows that have children.
1131: * @return an unmodifiableSortedSet of rows with children in index top-down order.
1132: */
1133: public SortedSet<Row> getRowsWithChildren() {
1134: return roRowsWithChildren;
1135: }
1136:
1137: @Override
1138: public boolean isFocusCapable() {
1139: Object parent = getParent();
1140: if (parent != null && !(parent instanceof Container))
1141: return false;
1142: return super .isFocusCapable();
1143: }
1144:
1145: @Override
1146: public void setFocusCapable(boolean focusCapable) {
1147: Object parent = getParent();
1148: if (parent != null && !(parent instanceof Container))
1149: throw new UnsupportedOperationException(
1150: getStandardPropertyUnsupportedMsg(
1151: PROPERTY_FOCUS_CAPABLE, false));
1152: super .setFocusCapable(focusCapable);
1153: }
1154:
1155: @Override
1156: public int getX() {
1157: Object parent = getParent();
1158: if (parent != null && !(parent instanceof Container))
1159: throw new UnsupportedOperationException(
1160: getStandardPropertyUnsupportedMsg(PROPERTY_X, true));
1161: return super .getX();
1162: }
1163:
1164: @Override
1165: public void setX(int x) {
1166: Object parent = getParent();
1167: if (parent != null && !(parent instanceof Container))
1168: throw new UnsupportedOperationException(
1169: getStandardPropertyUnsupportedMsg(PROPERTY_X, false));
1170: super .setX(x);
1171: }
1172:
1173: @Override
1174: public int getY() {
1175: Object parent = getParent();
1176: if (parent != null && !(parent instanceof Container))
1177: throw new UnsupportedOperationException(
1178: getStandardPropertyUnsupportedMsg(PROPERTY_Y, true));
1179: return super .getY();
1180: }
1181:
1182: @Override
1183: public void setY(int y) {
1184: Object parent = getParent();
1185: if (parent != null && !(parent instanceof Container))
1186: throw new UnsupportedOperationException(
1187: getStandardPropertyUnsupportedMsg(PROPERTY_Y, false));
1188: super.setY(y);
1189: }
1190: }
|