0001: /*
0002: * Copyright (c) 2004 JETA Software, Inc. All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without modification,
0005: * are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of JETA Software nor the names of its contributors may
0015: * be used to endorse or promote products derived from this software without
0016: * specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0021: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
0022: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0023: * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0024: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
0025: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0026: * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0028: */
0029:
0030: package com.jeta.forms.gui.form;
0031:
0032: import java.awt.BorderLayout;
0033: import java.awt.Component;
0034: import java.awt.Container;
0035: import java.awt.Rectangle;
0036: import java.util.Collection;
0037: import java.util.Iterator;
0038: import java.util.LinkedList;
0039:
0040: import javax.swing.JComponent;
0041: import javax.swing.JLayeredPane;
0042: import javax.swing.JPanel;
0043: import javax.swing.JScrollPane;
0044: import javax.swing.JViewport;
0045:
0046: import com.jeta.forms.gui.common.FormException;
0047: import com.jeta.forms.gui.common.FormSpecAdapter;
0048: import com.jeta.forms.gui.common.FormUtils;
0049: import com.jeta.forms.gui.components.ComponentFactory;
0050: import com.jeta.forms.gui.components.ComponentSource;
0051: import com.jeta.forms.gui.components.EmptyComponentFactory;
0052: import com.jeta.forms.gui.effects.Paintable;
0053: import com.jeta.forms.gui.effects.Painter;
0054: import com.jeta.forms.logger.FormsLogger;
0055: import com.jeta.forms.store.memento.FormGroupSet;
0056: import com.jeta.forms.store.properties.effects.PaintProperty;
0057: import com.jeta.forms.store.support.Matrix;
0058: import com.jeta.open.gui.framework.JETAPanel;
0059: import com.jeta.open.registry.JETARegistry;
0060: import com.jgoodies.forms.layout.CellConstraints;
0061: import com.jgoodies.forms.layout.ColumnSpec;
0062: import com.jgoodies.forms.layout.FormLayout;
0063: import com.jgoodies.forms.layout.RowSpec;
0064:
0065: /**
0066: * The <code>GridViewBase</code> is responsible for containing the parent
0067: * container that contains the components on the form as well as rendering the
0068: * grid lines during design mode as well as any background and fill effects for
0069: * grid cells. The GridViewBase employs a JLayeredPane to separate the various
0070: * elements of the view.
0071: *
0072: * <pre>
0073: * Containment Hierarchy:
0074: *
0075: * GridViewBase(this)
0076: * |
0077: * -- JLayeredPane (m_layered_pane)
0078: * |
0079: * -- GridViewBase.FormContainer (m_form <-- FormLayout )
0080: * | |
0081: * | -- GridComponent1
0082: * | |
0083: * | -- GridComponent2
0084: * | |
0085: * | -- GridComponentN
0086: * |
0087: * -- CellPainter (m_cell_painter)
0088: * |
0089: * -- BackgroundPainter ( m_background_painter - bottom layer )
0090: * </pre>
0091: *
0092: * @author Jeff Tassin
0093: */
0094: public class GridView extends JETAPanel implements Paintable,
0095: FormAccessor, GridCellListener {
0096: /**
0097: * The JGoodies FormLayout for this view.
0098: */
0099: private FormLayout m_formlayout;
0100:
0101: /**
0102: * The container that contains the GridComponents. This is also the
0103: * container whose layout manager is m_formlayout.
0104: */
0105: private FormContainer m_form;
0106:
0107: /**
0108: * The layered pane for this view [grid overlay] top [ form ] [ cell painter ]
0109: * [background effects] bottom
0110: */
0111: private JLayeredPane m_layered_pane;
0112:
0113: /**
0114: * Paints the background effects for a form (i.e. texture, gradient, solid
0115: * fill, image )
0116: */
0117: private BackgroundPainter m_background_painter;
0118:
0119: /**
0120: * Maintains a cache of row/column assignments for the GridComponents in
0121: * this view. This allows us to quickly lookup a component based on the
0122: * row/column position. Otherwise, we would need to do a linear lookup by
0123: * iterating over all components in the view. This would be too inefficient
0124: * when repainting.
0125: */
0126: private CellAssignmentCache m_assignment_cache = new CellAssignmentCache(
0127: this );
0128:
0129: /**
0130: * The object that renders the grid lines.
0131: */
0132: private GridOverlay m_overlay;
0133:
0134: /**
0135: * A cached LayoutInfo object from the FormLayout. This is needed because
0136: * the FormLayout returns a copy. This makes painting very slow, so we need
0137: * to cache it here.
0138: */
0139: private FormLayout.LayoutInfo m_layoutinfo = null;
0140:
0141: /**
0142: * Paints effects for individual cells in the grid.
0143: */
0144: private CellPainter m_cell_painter;
0145:
0146: /**
0147: * A matrix of PaintProperty objects that define the fill effects for
0148: * individual cells in the grid. The matrix cells correspond to the cells in
0149: * this view's grid. If a PaintProperty is found in the matrix at a given
0150: * column/row, then it is used to fill the corresponding cell in this view.
0151: */
0152: private Matrix m_cell_painters = new Matrix(0, 0);
0153:
0154: /**
0155: * Defines the the column groups for this form See:
0156: * {@link com.jgoodies.forms.layout.FormLayout#.setColumnGroups}
0157: */
0158: private FormGroupSet m_col_groups = new FormGroupSet();
0159:
0160: /**
0161: * Defines the row groups for this form. See:
0162: * {@link com.jgoodies.forms.layout.FormLayout#.setRowGroups}
0163: */
0164: private FormGroupSet m_row_groups = new FormGroupSet();
0165:
0166: /**
0167: * A timestamp of the last time a nested form in this container was
0168: * modified. This is primarily used to implement the fail-fast FormIterator
0169: */
0170: private long m_nested_mod_stamp = -1;
0171:
0172: /**
0173: * Flag that indicates if GridViewEvents should be fired.
0174: */
0175: private boolean m_events_enabled = false;
0176:
0177: /**
0178: * A list of GridViewListener objects that are interested in GridViewEvents
0179: * from this object.
0180: */
0181: private LinkedList m_listeners = new LinkedList();
0182:
0183: /**
0184: * The various layers in this view
0185: */
0186: public static final Integer BACKGROUND_PAINTER_LAYER = new Integer(
0187: 0);
0188: public static final Integer CELL_PAINTER_LAYER = new Integer(1);
0189: public static final Integer FORM_LAYER = new Integer(2);
0190: public static final Integer OVERLAY_LAYER = new Integer(9);
0191: public static final Integer FOCUS_LAYER = new Integer(10);
0192:
0193: /**
0194: * Creates a <code>GridView</code> with no rows and columns
0195: */
0196: public GridView() {
0197: setOpaque(true);
0198: }
0199:
0200: /**
0201: * Creates a <code>GridView</code> with the specified column and row
0202: * specifications. The view is initialized which also creates the columns
0203: * and rows.
0204: *
0205: * @param colspecs
0206: * the encoded column specs (comma delimited) for the form layout
0207: * @param rowspecs
0208: * the encoded row specs (comma delimited) for the form layout
0209: */
0210: public GridView(String colspecs, String rowspecs) {
0211: initialize(colspecs, rowspecs);
0212: }
0213:
0214: /**
0215: * Creates a <code>GridView</code> with the specified columns and rows.
0216: * The default column and row specifications are applied.
0217: *
0218: * @param cols
0219: * the number of columns to create
0220: * @param row
0221: * the number of rows to create.
0222: */
0223: public GridView(int cols, int rows) {
0224: initialize(cols, rows);
0225: }
0226:
0227: /**
0228: * Adds the component at the given row and column. If a component already
0229: * occupies cells specified by the constraints, it is not overwritten, and
0230: * the call will still succeed.
0231: *
0232: * @param gc
0233: * the grid component to add to this view.
0234: * @param cc
0235: * the cell constraints to apply to the component.
0236: */
0237: public void addComponent(GridComponent gc, CellConstraints cc) {
0238: gc.addListener(this );
0239: m_form.add(gc, cc);
0240: m_assignment_cache.addComponent(gc);
0241:
0242: m_form.revalidate();
0243: fireGridEvent(new GridViewEvent(this ,
0244: GridViewEvent.CELL_CHANGED));
0245: unitTest();
0246: }
0247:
0248: /**
0249: * Adds the component at the given row and column. If a component already
0250: * occupies cells specified by the constraints, it is not overwritten, and
0251: * the call will still succeed.
0252: *
0253: * @param gc
0254: * the grid component to add to this view
0255: * @param cc
0256: * the component constraints to apply to the component.
0257: */
0258: public void addComponent(GridComponent gc, ComponentConstraints cc) {
0259: addComponent(gc, cc.createCellConstraints());
0260: }
0261:
0262: /**
0263: * Adds a listener that wants to receive GridViewEvents from this view
0264: *
0265: * @param listener
0266: * the listener to add to this view.
0267: */
0268: public void addListener(GridViewListener listener) {
0269: if (listener != null && !m_listeners.contains(listener)) {
0270: m_listeners.add(listener);
0271: }
0272: }
0273:
0274: /**
0275: * cache the last component that was selected. This is needed for
0276: * performance.
0277: */
0278: private GridComponent m_last_comp;
0279:
0280: /**
0281: * GridCellListener implementation. Here we are getting events from
0282: * individual GridComponents owned by this view. Most events are simply
0283: * forwarded up the chain as GridViewEvents.
0284: */
0285: public void cellChanged(GridCellEvent evt) {
0286: if (evt.getId() == GridCellEvent.EDIT_COMPONENT) {
0287: fireGridEvent(new GridViewEvent(this ,
0288: GridViewEvent.EDIT_COMPONENT, evt));
0289: } else if (evt.getId() == GridCellEvent.CELL_SELECTED) {
0290: fireGridEvent(new GridViewEvent(this ,
0291: GridViewEvent.CELL_SELECTED, evt));
0292: m_last_comp = (GridComponent) evt.getSource();
0293: } else {
0294: fireGridEvent(new GridViewEvent(this ,
0295: GridViewEvent.CELL_CHANGED, evt));
0296: }
0297: }
0298:
0299: /**
0300: * Deselects all grid components in the view
0301: */
0302: public void deselectAll() {
0303: if (m_last_comp != null)
0304: m_last_comp.setSelected(false);
0305: }
0306:
0307: /**
0308: * Override so we can explicitly tell the form to layout as well.
0309: */
0310: public void doLayout() {
0311: super .doLayout();
0312: m_form.doLayout();
0313: }
0314:
0315: /**
0316: * Sets the flag that indicates if events should be fired.
0317: *
0318: * @param enable
0319: * set to true if GridViewEvents should be fired when this view
0320: * is changed.
0321: */
0322: public void enableEvents(boolean enable) {
0323: m_events_enabled = enable;
0324: }
0325:
0326: /**
0327: * Sends a GridViewEvent to all registered listeners. A GridViewEvent is
0328: * fired when the view changes or one of its child GridComponents changes.
0329: * This is only needed by the designer.
0330: *
0331: * @param evt
0332: * the event to post to all registered listeners.
0333: */
0334: public void fireGridEvent(GridViewEvent evt) {
0335: if (isEventsEnabled()) {
0336: if (evt.getId() != GridViewEvent.EDIT_COMPONENT
0337: && evt.getId() != GridViewEvent.CELL_SELECTED) {
0338: refreshView();
0339: }
0340:
0341: m_layoutinfo = null;
0342: Iterator iter = m_listeners.iterator();
0343: while (iter.hasNext()) {
0344: try {
0345: GridViewListener listener = (GridViewListener) iter
0346: .next();
0347: listener.gridChanged(evt);
0348: } catch (Exception e) {
0349: FormsLogger.debug(e);
0350: }
0351: }
0352: }
0353: }
0354:
0355: /**
0356: * Helper method that creates empty components. During design mode, a grid
0357: * is filled with empty components to provide a minimum space for each cell.
0358: * Without these components, the grid cells could have a zero width or
0359: * height when the size is set to <i>Preferred</i> for either the column
0360: * and/or row.
0361: *
0362: * @param view
0363: * the view to be initialized
0364: * @param compsrc
0365: * the component source that is used to help create empty
0366: * component objects.
0367: */
0368: public static void fillCells(GridView view, ComponentSource compsrc) {
0369: try {
0370: for (int col = 1; col <= view.getColumnCount(); col++) {
0371: for (int row = 1; row <= view.getRowCount(); row++) {
0372: EmptyComponentFactory factory = new EmptyComponentFactory(
0373: compsrc);
0374: StandardComponent gc = (StandardComponent) factory
0375: .createComponent("empty", view);
0376: view.addComponent(gc, new ReadOnlyConstraints(col,
0377: row));
0378: }
0379: }
0380: } catch (Exception e) {
0381: e.printStackTrace();
0382: }
0383: }
0384:
0385: /**
0386: * Returns the cell bounds at the given column and row.
0387: *
0388: * @param col
0389: * the 1-based column
0390: * @param row
0391: * the 1-based row
0392: * @return the cell bounds at the given row/column.
0393: */
0394: public Rectangle getCellBounds(int col, int row) {
0395: try {
0396: int org_x = getColumnOrgX(col);
0397: int org_y = getRowOrgY(row);
0398: int width = getColumnWidth(col);
0399: int height = getRowHeight(col);
0400: return new Rectangle(org_x, org_y, width, height);
0401: } catch (Exception e) {
0402: System.out
0403: .println("GridView.getCellBounds exception: col: "
0404: + col + " row: " + row);
0405: e.printStackTrace();
0406: return null;
0407: }
0408: }
0409:
0410: /**
0411: * Returns a Matrix object that contains the cell painter properties
0412: * (PaintProperty) for cells that have fill effects.
0413: *
0414: * @return A matrix of cell painters
0415: */
0416: public Matrix getCellPainters() {
0417: FormUtils
0418: .safeAssert(m_cell_painters.getRowCount() == getRowCount());
0419: FormUtils
0420: .safeAssert(m_cell_painters.getColumnCount() == getColumnCount());
0421: return m_cell_painters;
0422: }
0423:
0424: /**
0425: * Returns the number of columns in this view.
0426: *
0427: * @return the number of columns in this view
0428: */
0429: public int getColumnCount() {
0430: return m_formlayout.getColumnCount();
0431: }
0432:
0433: /**
0434: * Returns the left location in pixels of the given column.
0435: *
0436: * @param col
0437: * the 1-based column
0438: * @return the column x-origin for the given column.
0439: */
0440: public int getColumnOrgX(int col) {
0441: col--;
0442: FormLayout.LayoutInfo linfo = getLayoutInfo();
0443: return linfo.columnOrigins[col];
0444: }
0445:
0446: /**
0447: * Returns a FormGroupSet that specifies the column groups for this view.
0448: *
0449: * @return the column groups for this view
0450: */
0451: public FormGroupSet getColumnGroups() {
0452: return m_col_groups;
0453: }
0454:
0455: /**
0456: * Return a encoded string of all column specs for this view. Each column
0457: * spec is separated by a comma.
0458: *
0459: * @return the encoded ColumnSpecs for all rows in the view
0460: */
0461: public String getColumnSpecs() {
0462: StringBuffer sbuff = new StringBuffer();
0463: for (int col = 1; col <= getColumnCount(); col++) {
0464: ColumnSpec cs = getColumnSpec(col);
0465: if (col > 1)
0466: sbuff.append(",");
0467:
0468: String colspec = cs.toShortString();
0469: sbuff.append(FormSpecAdapter.fixup(colspec));
0470: }
0471:
0472: return sbuff.toString();
0473: }
0474:
0475: /**
0476: * Returns the column width in pixels of the given column.
0477: *
0478: * @param col
0479: * the 1-based column
0480: * @return the width for the given column.
0481: */
0482: public int getColumnWidth(int col) {
0483: col--;
0484: FormLayout.LayoutInfo linfo = getLayoutInfo();
0485: return linfo.columnOrigins[col + 1] - linfo.columnOrigins[col];
0486: }
0487:
0488: /**
0489: * Returns the ColumnSpec associated with the given column.
0490: *
0491: * @param col
0492: * the 1-based column
0493: * @return the ColumnSpec for the given column
0494: */
0495: public ColumnSpec getColumnSpec(int col) {
0496: return m_formlayout.getColumnSpec(col);
0497: }
0498:
0499: /**
0500: * Returns the FormAccessor associated with this view. A FormAccessor is
0501: * used to add/remove/iterate components on a form.
0502: */
0503: public FormAccessor getFormAccessor() {
0504: return this ;
0505: }
0506:
0507: /**
0508: * Do not call this method. It is used only for testing. Call iterator() if
0509: * you need to list the components in the view.
0510: *
0511: * @deprecated
0512: */
0513: public Container getFormContainer() {
0514: return m_form;
0515: }
0516:
0517: /**
0518: * Returns the name assigned to the form component associated with this
0519: * accessor.
0520: *
0521: * @return the name assigned to the form component associated with this
0522: * accessor.
0523: */
0524: public String getFormName() {
0525: return getName();
0526: }
0527:
0528: /**
0529: * Returns the timestamp of the last time a child component was added or
0530: * removed from the form associated with this view. This is primarily used
0531: * to implement the fail-fast FormIterator
0532: *
0533: * @return the timestamp of the last time this container was modified.
0534: */
0535: long getModificationStamp() {
0536: return m_form.getModificationStamp();
0537: }
0538:
0539: /**
0540: * Returns the timestamp of the last time a child component was added or
0541: * removed from a nested form associated with this view. This is primarily
0542: * used to implement the fail-fast FormIterator
0543: *
0544: * @return the timestamp of the last time a nested form in this view was
0545: * modified.
0546: */
0547: long getNestedModificationStamp() {
0548: return m_nested_mod_stamp;
0549: }
0550:
0551: /**
0552: * Returns the number of rows in this view.
0553: *
0554: * @return the number of rows in this view
0555: */
0556: public int getRowCount() {
0557: return m_formlayout.getRowCount();
0558: }
0559:
0560: /**
0561: * Return the layout manager (FormLayout) for the form.
0562: *
0563: * @return the underlying form layout object.
0564: */
0565: public FormLayout getFormLayout() {
0566: return m_formlayout;
0567: }
0568:
0569: /**
0570: * Return the total width of the form as calculated by the FormLayout
0571: * manager.
0572: *
0573: * @return the width of the form as determined by the FormLayout manager
0574: */
0575: public int getFormWidth() {
0576: FormLayout.LayoutInfo linfo = getLayoutInfo();
0577: return linfo.getWidth();
0578: }
0579:
0580: /**
0581: * Return the total height of the form as calculated by the FormLayout
0582: * manager.
0583: *
0584: * @return the height of the form as determined by the FormLayout manager
0585: */
0586: public int getFormHeight() {
0587: FormLayout.LayoutInfo linfo = getLayoutInfo();
0588: return linfo.getHeight();
0589: }
0590:
0591: /**
0592: * Returns the GridComponent that occupies the cell at the given row/column.
0593: * Null is returned if a component does not occupy the cell.
0594: *
0595: * @return the grid component that occupies the cell at the given row/column
0596: */
0597: public GridComponent getGridComponent(int col, int row) {
0598: return m_assignment_cache.getGridComponent(col, row);
0599: }
0600:
0601: /**
0602: * Returns the total number of GridComponent objects contained by this form.
0603: *
0604: * @return the number of grid components in this view
0605: */
0606: public int getGridComponentCount() {
0607: return m_form.getComponentCount();
0608: }
0609:
0610: /**
0611: * Returns the GridComponent at the given index in the parent container.
0612: *
0613: * @param index
0614: * the 0-based index in the parent container.
0615: * @return the grid component at the given index.
0616: */
0617: public GridComponent getGridComponent(int index) {
0618: Object obj = m_form.getComponent(index);
0619: if (obj instanceof GridComponent)
0620: return (GridComponent) obj;
0621: else
0622: return null;
0623: }
0624:
0625: /**
0626: * Return the FormLayout.LayoutInfo.
0627: *
0628: * @return the FormLayout.LayoutInfo. See:
0629: * {@link com.jgoodies.forms.layout.FormLayout.LayoutInfo }
0630: */
0631: FormLayout.LayoutInfo getLayoutInfo() {
0632: /**
0633: * We need to cache this object because the FormLayout makes a copy
0634: * everytime getLayoutInfo is called. This slows down the system when
0635: * repainting or dragging the mouse.
0636: */
0637: if (m_layoutinfo == null)
0638: m_layoutinfo = m_formlayout.getLayoutInfo(m_form);
0639:
0640: return m_layoutinfo;
0641: }
0642:
0643: /**
0644: * Return the fill property for the cell at the given column and row.
0645: *
0646: * @param col
0647: * the 1-based column
0648: * @param row
0649: * the 1-based row
0650: * @return the painter at the given column and row. Null is returned if no
0651: * painter exists for that cell.
0652: */
0653: public PaintProperty getPaintProperty(int col, int row) {
0654: Object obj = m_cell_painters.getValue(row - 1, col - 1);
0655: if (obj instanceof PaintProperty) {
0656: PaintProperty pp = (PaintProperty) obj;
0657: if (pp != null)
0658: return pp;
0659: else
0660: return null;
0661: } else {
0662: if (obj != null) {
0663: System.out
0664: .println("GridView.getPaintProperty failed for col: "
0665: + col
0666: + " row: "
0667: + row
0668: + " prop: "
0669: + obj.getClass());
0670: }
0671: return null;
0672: }
0673: }
0674:
0675: /**
0676: * Returns the FormComponent that contains this GridView. Since the parent
0677: * of this view is not a FormComponent, the component heirarhcy must be
0678: * traversed until the first FormComponent instance is found.
0679: *
0680: * @return the FormComponent that contains this view.
0681: */
0682: public FormComponent getParentForm() {
0683: /**
0684: * the first parent should be a JETABean. The grandparent is actually
0685: * the FormComponent.
0686: */
0687: Component cc = getParent();
0688: while (cc != null && !(cc instanceof java.awt.Window)) {
0689: if (cc instanceof FormComponent) {
0690: FormComponent fc = (FormComponent) cc;
0691: FormUtils
0692: .safeAssert(fc.getBean().getDelegate() == this );
0693: return fc;
0694: }
0695: cc = cc.getParent();
0696: }
0697: FormUtils.safeAssert(false);
0698: return null;
0699: }
0700:
0701: /**
0702: * Returns the row height in pixels of the given row.
0703: *
0704: * @param row
0705: * the 1-based row
0706: * @return the row height for the given row.
0707: */
0708: public int getRowHeight(int row) {
0709: row--;
0710: FormLayout.LayoutInfo linfo = getLayoutInfo();
0711: return linfo.rowOrigins[row + 1] - linfo.rowOrigins[row];
0712: }
0713:
0714: /**
0715: * Returns the row origin in pixels of the given row.
0716: *
0717: * @param row
0718: * the 1-based row
0719: * @return the row y-origin for the given row.
0720: */
0721: public int getRowOrgY(int row) {
0722: row--;
0723: FormLayout.LayoutInfo linfo = getLayoutInfo();
0724: return linfo.rowOrigins[row];
0725: }
0726:
0727: /**
0728: * Returns the GridComponent that overlaps the cell at the given column and
0729: * row.
0730: *
0731: * @return the component that overlaps the cell at the given column and row.
0732: * If no component overlaps, then null is returned.
0733: */
0734: public GridComponent getOverlappingComponent(int col, int row) {
0735: return m_assignment_cache.getOverlappingComponent(col, row);
0736: }
0737:
0738: /**
0739: * Returns the GridOverlay associated with this view. The GridOverlay is
0740: * responsible for rendering the grid lines when in design mode.
0741: *
0742: * @return the GridOverlay associated with this view.
0743: */
0744: public GridOverlay getOverlay() {
0745: return m_overlay;
0746: }
0747:
0748: /**
0749: * Returns a FormGroupSet that specifies the row groups for this view.
0750: *
0751: * @return the row groups for this view
0752: */
0753: public FormGroupSet getRowGroups() {
0754: return m_row_groups;
0755: }
0756:
0757: /**
0758: * Returns the row spec for the specified row.
0759: *
0760: * @param row
0761: * the 1-based row
0762: * @return the RowSpec for the given row.
0763: */
0764: public RowSpec getRowSpec(int row) {
0765: FormUtils.safeAssert(row >= 1 && row <= getRowCount());
0766: return m_formlayout.getRowSpec(row);
0767: }
0768:
0769: /**
0770: * Return a encoded string of all row specs for this view. Each orow spec is
0771: * separated by a comma.
0772: *
0773: * @return the encoded RowSpecs for all rows in the view
0774: */
0775: public String getRowSpecs() {
0776: StringBuffer sbuff = new StringBuffer();
0777: for (int row = 1; row <= getRowCount(); row++) {
0778: RowSpec rs = getRowSpec(row);
0779: if (row > 1)
0780: sbuff.append(",");
0781:
0782: String rowspec = rs.toString();
0783: sbuff.append(FormSpecAdapter.fixup(rowspec));
0784: }
0785: return sbuff.toString();
0786: }
0787:
0788: /**
0789: * Returns the first selected GridComponent found in the component hierarhcy
0790: * of this container. Components in nested forms are checked as well.
0791: *
0792: * @returns the first selected component it finds in the component hierarhcy
0793: * of this container.
0794: */
0795: public GridComponent getSelectedComponent() {
0796: Iterator iter = gridIterator();
0797: while (iter.hasNext()) {
0798: GridComponent gc = (GridComponent) iter.next();
0799: if (gc instanceof FormComponent) {
0800: FormComponent cc = (FormComponent) gc;
0801: gc = cc.getSelectedComponent();
0802: if (gc != null)
0803: return gc;
0804: } else if (gc instanceof FormContainerComponent) {
0805: FormContainerComponent cc = (FormContainerComponent) gc;
0806: gc = cc.getSelectedComponent();
0807: if (gc != null)
0808: return gc;
0809: } else {
0810: if (gc.isSelected())
0811: return gc;
0812: }
0813: }
0814: return null;
0815: }
0816:
0817: /**
0818: * Call this only once to initialize to the default settings for a given
0819: * number of rows and columns
0820: *
0821: * @param cols
0822: * the number of columns in the view
0823: * @param rows
0824: * the number of rows in the view
0825: */
0826: public void initialize(int cols, int rows) {
0827: enableEvents(FormUtils.isDesignMode());
0828: StringBuffer colspec = new StringBuffer();
0829: for (int col = 1; col <= cols; col++) {
0830: if (col > 1)
0831: colspec.append(",");
0832: colspec.append("f:d:n");
0833:
0834: }
0835: StringBuffer rowspec = new StringBuffer();
0836: for (int row = 1; row <= rows; row++) {
0837: if (row > 1)
0838: rowspec.append(",");
0839: rowspec.append("c:d:n");
0840: }
0841: initialize(colspec.toString(), rowspec.toString());
0842: m_cell_painters = new Matrix(getRowCount(), getColumnCount());
0843: }
0844:
0845: /**
0846: * Initializes the view. This should be called only once.
0847: *
0848: * @param colspecs
0849: * the encoded column specs for the form layout
0850: * @param rowspecs
0851: * the encoded row specs for the form layout
0852: */
0853: void initialize(String colspecs, String rowspecs) {
0854: /**
0855: * This should not be needed since initialize should only be called
0856: * once. Just in case.
0857: */
0858: removeAll();
0859:
0860: /**
0861: * We need to handle the case where the FormLayout stores the encoded
0862: * string in a different form than it takes in the constructor. For
0863: * example, the FormLayout stores dlu sizes as dluX or dluY. However,
0864: * the FormSpec constructor cannot handle this form.
0865: */
0866: colspecs = FormSpecAdapter.fixupSpecs(colspecs);
0867: rowspecs = FormSpecAdapter.fixupSpecs(rowspecs);
0868:
0869: FormUtils.safeAssert(m_formlayout == null);
0870: setOpaque(true);
0871:
0872: m_formlayout = new FormLayout(colspecs, rowspecs);
0873: m_form = new FormContainer();
0874: m_form.setOpaque(false);
0875: m_form.setLayout(m_formlayout);
0876:
0877: setLayout(new BorderLayout());
0878:
0879: m_layered_pane = new JLayeredPane();
0880: m_layered_pane.setOpaque(false);
0881: m_layered_pane.setLayout(new JETALayerLayout());
0882:
0883: m_background_painter = new BackgroundPainter();
0884: m_cell_painter = new CellPainter(this );
0885:
0886: m_layered_pane.add(m_background_painter,
0887: BACKGROUND_PAINTER_LAYER);
0888: m_layered_pane.add(m_cell_painter, CELL_PAINTER_LAYER);
0889: m_layered_pane.add(m_form, FORM_LAYER);
0890: super .add(m_layered_pane, BorderLayout.CENTER);
0891:
0892: if (FormUtils.isDesignMode()) {
0893: GridOverlayFactory factory = (GridOverlayFactory) JETARegistry
0894: .lookup(GridOverlayFactory.COMPONENT_ID);
0895: if (factory != null) {
0896: m_overlay = factory.createOverlay(this );
0897: ((JComponent) getFormContainer())
0898: .setBorder(javax.swing.BorderFactory
0899: .createEmptyBorder(0, 0, 1, 1));
0900: m_layered_pane.add((JComponent) m_overlay,
0901: OVERLAY_LAYER);
0902: } else {
0903: FormUtils.safeAssert(false);
0904: }
0905: }
0906: }
0907:
0908: /**
0909: * Inserts a new column into the view at the given column index.
0910: *
0911: * @param col
0912: * the 1-based column index where the new column will be
0913: * inserted.
0914: * @param colspec
0915: * the specification for the new column.
0916: * @param factory
0917: * a factory used to create components to insert into the empty
0918: * cells of the newly created column. This is generally an
0919: * EmptyComponentFactory.
0920: */
0921: public void insertColumn(int col, ColumnSpec colspec,
0922: ComponentFactory factory) throws FormException {
0923: int old_col_count = getColumnCount();
0924: /**
0925: * first instantiate all components in case an exception is thrown we
0926: * don't have an empty column
0927: */
0928: LinkedList comps = new LinkedList();
0929: for (int row = 1; row <= getRowCount(); row++) {
0930: GridComponent gc = factory.createComponent("test", this );
0931: comps.add(gc);
0932: }
0933:
0934: CellConstraints cc = new CellConstraints();
0935: FormLayout layout = getFormLayout();
0936:
0937: if (col > getColumnCount()) {
0938: col = getColumnCount() + 1;
0939: layout.appendColumn(colspec);
0940: } else
0941: layout.insertColumn(col, colspec);
0942:
0943: if (FormUtils.isDebug()) {
0944: Iterator iter = gridIterator();
0945: while (iter.hasNext()) {
0946: GridComponent curr_gc = (GridComponent) iter.next();
0947: if (curr_gc.getColumn() == col) {
0948: System.out
0949: .println("GridView.insertRow failed at: col: "
0950: + col
0951: + " row: "
0952: + curr_gc.getRow()
0953: + " gc: " + curr_gc);
0954: }
0955: }
0956: }
0957:
0958: Iterator iter = comps.iterator();
0959: int row = 1;
0960: while (iter.hasNext()) {
0961: GridComponent gc = (GridComponent) iter.next();
0962: addComponent(gc, cc.xy(col, row));
0963: row++;
0964: }
0965:
0966: m_cell_painters.insertColumn(col - 1);
0967:
0968: FormUtils.safeAssert(getColumnCount() == (old_col_count + 1));
0969: FormUtils
0970: .safeAssert(getGridComponentCount() == getColumnCount()
0971: * getRowCount());
0972:
0973: updateColumnGroups();
0974: fireGridEvent(new GridViewEvent(this ,
0975: GridViewEvent.COLUMN_ADDED));
0976: }
0977:
0978: /**
0979: * Inserts a new row into the view at the given column index.
0980: *
0981: * @param row
0982: * the 1-based row index where the new row will be inserted.
0983: * @param rowspec
0984: * the specification for the new row.
0985: * @param factory
0986: * a factory used to create components to insert into the empty
0987: * cells of the newly created row. This is generally an
0988: * EmptyComponentFactory.
0989: */
0990: public void insertRow(int row, RowSpec rowspec,
0991: ComponentFactory factory) throws FormException {
0992: /**
0993: * first instantiate all components in case an exception is thrown we
0994: * don't have an empty row
0995: */
0996: int old_row_count = getRowCount();
0997: LinkedList comps = new LinkedList();
0998: for (int col = 1; col <= getColumnCount(); col++) {
0999: GridComponent gc = factory.createComponent("", this );
1000: comps.add(gc);
1001: }
1002:
1003: CellConstraints cc = new CellConstraints();
1004: FormLayout layout = getFormLayout();
1005: if (row > getRowCount()) {
1006: row = getRowCount() + 1;
1007: layout.appendRow(rowspec);
1008: } else
1009: layout.insertRow(row, rowspec);
1010:
1011: if (FormUtils.isDebug()) {
1012: Iterator iter = gridIterator();
1013: while (iter.hasNext()) {
1014: GridComponent curr_gc = (GridComponent) iter.next();
1015: if (curr_gc.getRow() == row) {
1016: System.out
1017: .println("GridView.insertRow failed at: col: "
1018: + curr_gc.getColumn()
1019: + " row: "
1020: + row + " gc: " + curr_gc);
1021: }
1022: }
1023: }
1024:
1025: Iterator iter = comps.iterator();
1026: int col = 1;
1027: while (iter.hasNext()) {
1028: // @todo provide a better naming strategy
1029: GridComponent gc = (GridComponent) iter.next();
1030: addComponent(gc, cc.xy(col, row));
1031: col++;
1032: }
1033:
1034: m_cell_painters.insertRow(row - 1);
1035:
1036: FormUtils.safeAssert(getRowCount() == (old_row_count + 1));
1037: FormUtils
1038: .safeAssert(getGridComponentCount() == getColumnCount()
1039: * getRowCount());
1040: updateRowGroups();
1041: fireGridEvent(new GridViewEvent(this , GridViewEvent.ROW_ADDED));
1042: }
1043:
1044: /**
1045: * Return true if GridViewEvents will be fired from the view.
1046: *
1047: * @return the flag that indicates if events should be fired.
1048: */
1049: public boolean isEventsEnabled() {
1050: return m_events_enabled;
1051: }
1052:
1053: /**
1054: * Return true if the grid lines are visible. This is only needed by the
1055: * designer.
1056: *
1057: * @return the grid overlay as visible/invisible
1058: */
1059: public boolean isGridVisible() {
1060: return m_overlay.isGridVisible();
1061: }
1062:
1063: /**
1064: * Returns an interator that iterates over the grid components
1065: * (GridComponent objects) in this view.
1066: *
1067: * @return an interator that iterates over the grid components in this view.
1068: */
1069: public Iterator gridIterator() {
1070: return new ComponentIterator();
1071: }
1072:
1073: /**
1074: * Creates a collection of all child grid components (GridComponent objects)
1075: * in this view. This method only returns GridComponent objects.
1076: *
1077: * @return a collection of GridComponents contained by this view.
1078: */
1079: public Collection listComponents() {
1080: LinkedList list = new LinkedList();
1081: Iterator iter = gridIterator();
1082: while (iter.hasNext()) {
1083: Object obj = iter.next();
1084: FormUtils.safeAssert(obj instanceof GridComponent);
1085: list.add(obj);
1086: }
1087: return list;
1088: }
1089:
1090: /**
1091: * Updates any parameters that need to be updated if the view changed such
1092: * as adding/deleting/editing a component, row, or column.
1093: */
1094: public void refreshView() {
1095: syncCellAssignments();
1096: revalidate();
1097: repaint();
1098: }
1099:
1100: /**
1101: * Removes the given column from the layout/view. Note that all components
1102: * in the column are removed as well.
1103: *
1104: * @param column
1105: * the 1-based index of the column to remove.
1106: */
1107: public void removeColumn(int column) {
1108: if (column < 1 || column > getColumnCount()) {
1109: FormUtils.safeAssert(false);
1110: return;
1111: }
1112: m_cell_painters.removeColumn(column - 1);
1113:
1114: LinkedList comps = new LinkedList();
1115: Iterator iter = gridIterator();
1116: while (iter.hasNext()) {
1117: GridComponent gc = (GridComponent) iter.next();
1118: if (gc.getColumn() == column) {
1119: comps.add(gc);
1120: }
1121: }
1122:
1123: iter = comps.iterator();
1124: while (iter.hasNext()) {
1125: GridComponent gc = (GridComponent) iter.next();
1126: m_form.remove(gc);
1127: }
1128: m_formlayout.removeColumn(column);
1129: updateColumnGroups();
1130: fireGridEvent(new GridViewEvent(this ,
1131: GridViewEvent.COLUMN_DELETED));
1132: }
1133:
1134: /**
1135: * Removes the given listener for the set of listeners that want events from
1136: * this view
1137: *
1138: * @param listener
1139: * the listener to remove
1140: */
1141: public void removeListener(GridViewListener listener) {
1142: if (listener != null)
1143: m_listeners.remove(listener);
1144: }
1145:
1146: /**
1147: * Removes the specified row from the layout/view. Note that all components
1148: * in the row are removed as well.
1149: *
1150: * @param row
1151: * the 1-based row to remove from the view.
1152: */
1153: public void removeRow(int row) {
1154:
1155: if (row < 1 || row > getRowCount()) {
1156: FormUtils.safeAssert(false);
1157: return;
1158: }
1159:
1160: m_cell_painters.removeRow(row - 1);
1161:
1162: LinkedList comps = new LinkedList();
1163: Iterator iter = gridIterator();
1164: while (iter.hasNext()) {
1165: GridComponent gc = (GridComponent) iter.next();
1166: if (gc.getRow() == row) {
1167: comps.add(gc);
1168: }
1169: }
1170:
1171: iter = comps.iterator();
1172: while (iter.hasNext()) {
1173: GridComponent gc = (GridComponent) iter.next();
1174: m_form.remove(gc);
1175: }
1176: m_formlayout.removeRow(row);
1177: updateRowGroups();
1178: fireGridEvent(new GridViewEvent(this , GridViewEvent.ROW_DELETED));
1179: }
1180:
1181: /**
1182: * Replaces a component in the view. Note that components cannot be deleted
1183: * in the design view. They must be replaced with 'Empty' components if you
1184: * want to simulate an empty cell.
1185: */
1186: public void replaceComponent(GridComponent newComp,
1187: GridComponent oldComp) {
1188: CellConstraints cc = oldComp.getConstraints()
1189: .createCellConstraints();
1190: setComponent(newComp, cc);
1191: fireGridEvent(new GridViewEvent(this ,
1192: GridViewEvent.CELL_CHANGED));
1193: }
1194:
1195: /**
1196: * Override so we can explicitly tell the child form to revalidate as well.
1197: */
1198: public void revalidate() {
1199: if (m_form != null) {
1200: m_form.revalidate();
1201: }
1202: super .revalidate();
1203: }
1204:
1205: /**
1206: * Sets an object used to render the fill effect for the entire form
1207: * associated with this view.
1208: *
1209: * @param p
1210: * the painter to set for this view.
1211: */
1212: public void setBackgroundPainter(Painter p) {
1213: if (m_background_painter != null)
1214: m_background_painter.setBackgroundPainter(p);
1215: }
1216:
1217: /**
1218: * Override setBounds so we can reset the layoutinfo.
1219: */
1220: public void setBounds(int x, int y, int width, int height) {
1221: super .setBounds(x, y, width, height);
1222: m_layoutinfo = null;
1223: }
1224:
1225: /**
1226: * Sets the cell painters associated with this view
1227: *
1228: * @param painters
1229: * a matrix of cell painters (PaintProperty objects)
1230: */
1231: public void setCellPainters(Matrix painters) {
1232: if (painters == null) {
1233: m_cell_painters = new Matrix(getRowCount(),
1234: getColumnCount());
1235: } else {
1236: m_cell_painters = painters;
1237: if ((painters.getRowCount() != getRowCount())
1238: || (painters.getColumnCount()) != (getColumnCount()))
1239: m_cell_painters = new Matrix(getRowCount(),
1240: getColumnCount());
1241: }
1242: }
1243:
1244: /**
1245: * Updates the columnspecrowspec for the given column
1246: *
1247: * @param colspec
1248: * the new columnspec
1249: * @param col
1250: * the column
1251: */
1252: public void setColumnSpec(int col, ColumnSpec colspec) {
1253: FormLayout layout = getFormLayout();
1254: ColumnSpec newspec = new ColumnSpec(colspec
1255: .getDefaultAlignment(), colspec.getSize(), colspec
1256: .getResizeWeight());
1257: layout.setColumnSpec(col, newspec);
1258: m_form.revalidate();
1259: fireGridEvent(new GridViewEvent(this ,
1260: GridViewEvent.COLUMN_SPEC_CHANGED));
1261: }
1262:
1263: /**
1264: * Sets the component at the given row and column. Removes any existing
1265: * components that occupy cells which will contain the new component.
1266: */
1267: public void setComponent(GridComponent gc, CellConstraints cc) {
1268:
1269: GridComponent oldcomp = getGridComponent(cc.gridX, cc.gridY);
1270: if (oldcomp != null) {
1271: oldcomp.removeListener(this );
1272: m_form.remove(oldcomp);
1273: }
1274: gc.addListener(this );
1275: m_form.add(gc, cc);
1276: m_form.revalidate();
1277: syncCellAssignments();
1278:
1279: fireGridEvent(new GridViewEvent(this ,
1280: GridViewEvent.CELL_CHANGED));
1281: unitTest();
1282: }
1283:
1284: /**
1285: * Sets the component at the given row and column. Removes any existing
1286: * components that occupy cells which will contain the new component. The
1287: * component constraints determines the row and column.
1288: *
1289: * @param gc
1290: * the grid component
1291: * @param cc
1292: * the component constraints to set.
1293: */
1294: public void setComponent(GridComponent gc, ComponentConstraints cc) {
1295: setComponent(gc, cc.createCellConstraints());
1296: }
1297:
1298: /**
1299: * Sets the constraints for the given component. Note that you should not
1300: * change the column and row assignments using this method.
1301: *
1302: * @param gc
1303: * the grid component
1304: * @param cc
1305: * the cell constraints to set.
1306: */
1307: public void setConstraints(GridComponent gc, CellConstraints cc) {
1308: m_formlayout.setConstraints(gc, cc);
1309: syncCellAssignments();
1310: m_form.revalidate();
1311: fireGridEvent(new GridViewEvent(this ,
1312: GridViewEvent.CELL_CHANGED, gc));
1313: }
1314:
1315: /**
1316: * Sets the constraints for the given component. Note that you should not
1317: * change the column and row assignments using this method.
1318: *
1319: * @param gc
1320: * the grid component
1321: * @param cc
1322: * the component constraints to set.
1323: */
1324: public void setConstraints(GridComponent gc, ComponentConstraints cc) {
1325: setConstraints(gc, cc.createCellConstraints());
1326: }
1327:
1328: /**
1329: * Sets the grid overlay as visible/invisible. This is only needed during
1330: * design mode.
1331: *
1332: * @param bvis
1333: * set to true to show the grid. set to false to hide the grid.
1334: */
1335: public void setGridVisible(boolean bvis) {
1336: m_overlay.setGridVisible(bvis);
1337: }
1338:
1339: /**
1340: * Sets the paint property for the given cell. This is the property the is
1341: * responsible for the fill effect for the cell.
1342: *
1343: * @param col
1344: * the 1-based column
1345: * @param row
1346: * the 1-based row
1347: */
1348: public void setPaintProperty(int col, int row, PaintProperty pp) {
1349: m_cell_painters.setValue(row - 1, col - 1, pp);
1350: repaint();
1351: }
1352:
1353: /**
1354: * Sets the column groups for this form. This call is ignored if the group
1355: * contains columns that are invalid.
1356: *
1357: * @param groups
1358: * the column groups to set for this form.
1359: */
1360: public void setColumnGroups(FormGroupSet groups) {
1361: if (groups != null) {
1362: int[][] grp_idxs = groups.toArray();
1363: for (int index = 0; index < grp_idxs.length; index++) {
1364: int[] col_grp = grp_idxs[index];
1365: for (int colidx = 0; colidx < col_grp.length; colidx++) {
1366: int col = col_grp[colidx];
1367: if (col < 1 || col > getColumnCount()) {
1368: FormUtils.safeAssert(false);
1369: return;
1370: }
1371: }
1372: }
1373: m_formlayout.setColumnGroups(grp_idxs);
1374: m_col_groups = groups;
1375: fireGridEvent(new GridViewEvent(this ,
1376: GridViewEvent.COLUMN_GROUPS_CHANGED));
1377: }
1378: }
1379:
1380: /**
1381: * Sets the row groups for this form. This call is ignored if the group
1382: * contains rows that are invalid.
1383: *
1384: * @param groups
1385: * the row groups for this form.
1386: */
1387: public void setRowGroups(FormGroupSet groups) {
1388: if (groups != null) {
1389: int[][] grp_idxs = groups.toArray();
1390: for (int index = 0; index < grp_idxs.length; index++) {
1391: int[] row_grp = grp_idxs[index];
1392: for (int rowidx = 0; rowidx < row_grp.length; rowidx++) {
1393: int row = row_grp[rowidx];
1394: if (row < 1 || row > getRowCount()) {
1395: FormUtils.safeAssert(false);
1396: return;
1397: }
1398: }
1399: }
1400:
1401: m_formlayout.setRowGroups(grp_idxs);
1402: m_row_groups = groups;
1403: fireGridEvent(new GridViewEvent(this ,
1404: GridViewEvent.ROW_GROUPS_CHANGED));
1405: }
1406: }
1407:
1408: /**
1409: * Updates the rowspec for the given row
1410: *
1411: * @param rowspec
1412: * the new rowpsec
1413: * @param row
1414: * the row
1415: */
1416: public void setRowSpec(int row, RowSpec rowspec) {
1417: FormLayout layout = getFormLayout();
1418:
1419: RowSpec newspec = new RowSpec(rowspec.getDefaultAlignment(),
1420: rowspec.getSize(), rowspec.getResizeWeight());
1421: layout.setRowSpec(row, newspec);
1422: m_form.revalidate();
1423: fireGridEvent(new GridViewEvent(this ,
1424: GridViewEvent.ROW_SPEC_CHANGED));
1425: }
1426:
1427: /**
1428: * Tells the CellAssignmentCache to update itself
1429: */
1430: private void syncCellAssignments() {
1431: m_assignment_cache.sync();
1432: }
1433:
1434: /**
1435: * FormAccessor implementation. Adds a bean to this container using the
1436: * given constraints.
1437: *
1438: * @param comp
1439: * the bean to add to the form.
1440: * @param cc
1441: * the constraints for the bean. This must be a valid
1442: * CellConstraints instance.
1443: */
1444: public void addBean(Component comp, CellConstraints cc) {
1445: if (comp == null) {
1446: System.err.println("addBean: comp cannot be null");
1447: return;
1448: }
1449:
1450: if (cc == null) {
1451: System.err.println("addBean: cc cannot be null");
1452: return;
1453: }
1454:
1455: m_form.add(comp, cc);
1456: refreshView();
1457: }
1458:
1459: /**
1460: * Return the cell constraints associated with the given component. The call
1461: * is merely forwarded to the FormLayout instance.
1462: *
1463: * @return the constraints associated with the given component.
1464: * @throws NullPointerException
1465: * if component is null or has not been added to the Container
1466: */
1467: public CellConstraints getConstraints(Component comp) {
1468: return getFormLayout().getConstraints(comp);
1469: }
1470:
1471: /**
1472: * Return the actual container that has the given layout. This method should
1473: * rarely be called. It is only provided for very limited cases.
1474: *
1475: * @return the container associated with the FormLayout
1476: */
1477: public Container getContainer() {
1478: return getFormContainer();
1479: }
1480:
1481: /**
1482: * Defaults to beanIterator(false). See {@link #beanIterator(boolean)}
1483: *
1484: * @return an iterator to all the Java Beans in this container. Beans in
1485: * nested containers are not included.
1486: */
1487: public Iterator beanIterator() {
1488: return new FormIterator(this , false);
1489: }
1490:
1491: /**
1492: * Returns an iterator to all Java Beans in this container. This iterator
1493: * also supports iterating over nested containers if the nested flag is set
1494: * to true.
1495: *
1496: * @param nested
1497: * if true, all components in nested forms will be returned.
1498: * @return an iterator to all the Java Beans in this container. Beans in
1499: * nested containers are included if nested is true.
1500: */
1501: public Iterator beanIterator(boolean nested) {
1502: return new FormIterator(this , nested);
1503: }
1504:
1505: /**
1506: * Removes a bean from the container associated with this accessor.
1507: *
1508: * @param comp
1509: * the component to remove.
1510: * @return the component that was removed. If this method fails for any
1511: * reason then null is returned.
1512: */
1513: public Component removeBean(Component comp) {
1514: if (comp == null) {
1515: System.err.println("removeBean: comp cannot be null");
1516: return null;
1517: }
1518:
1519: return replaceBean(comp, null);
1520: }
1521:
1522: /**
1523: * FormAccessor implementation. Removes a bean with the given name from this
1524: * container.
1525: *
1526: * @param compName
1527: * the name of the Java Bean to remove.
1528: * @return the component that was removed. If this method fails for any
1529: * reason then null is returned.
1530: */
1531: public Component removeBean(String compName) {
1532: if (compName == null) {
1533: System.err.println("removeBean: compName cannot be null");
1534: return null;
1535: }
1536:
1537: for (int index = 0; index < m_form.getComponentCount(); index++) {
1538: Component comp = m_form.getComponent(index);
1539: if (compName.equals(comp.getName())) {
1540: m_form.remove(comp);
1541: refreshView();
1542: return comp;
1543: } else if (comp instanceof GridComponent) {
1544: GridComponent gc = (GridComponent) comp;
1545: Component bean = gc.getBeanChildComponent();
1546: if (bean != null && compName.equals(bean.getName())) {
1547: m_form.remove(comp);
1548: refreshView();
1549: return comp;
1550: } else if (bean instanceof JScrollPane) {
1551: JScrollPane scroll = (JScrollPane) bean;
1552: JViewport viewport = scroll.getViewport();
1553: if (viewport != null) {
1554: bean = viewport.getView();
1555: if (bean != null
1556: && compName.equals(bean.getName())) {
1557: m_form.remove(scroll);
1558: refreshView();
1559: return scroll;
1560: }
1561: }
1562: }
1563:
1564: } else if (comp instanceof JScrollPane) {
1565: JScrollPane scroll = (JScrollPane) comp;
1566: JViewport viewport = scroll.getViewport();
1567: if (viewport != null) {
1568: Component bean = viewport.getView();
1569: if (bean != null && compName.equals(bean.getName())) {
1570: m_form.remove(comp);
1571: refreshView();
1572: return comp;
1573: }
1574: }
1575: }
1576: }
1577: return null;
1578: }
1579:
1580: /**
1581: * FormAccessor implementation. Replaces an existing bean with a new bean.
1582: * If the existing bean is not found, the request is ignored.
1583: *
1584: * @param oldComp
1585: * the component to replace
1586: * @param newComponent
1587: * the component to add.
1588: * @return the component that was replaced. If this method fails for any
1589: * reason then null is returned.
1590: */
1591: public Component replaceBean(Component oldComp,
1592: Component newComponent) {
1593: if (oldComp == null) {
1594: System.err
1595: .println("replaceBean: oldComponent cannot be null");
1596: return null;
1597: }
1598:
1599: Component comp = null;
1600: CellConstraints cc = null;
1601:
1602: for (int index = 0; index < m_form.getComponentCount(); index++) {
1603: comp = m_form.getComponent(index);
1604: cc = m_formlayout.getConstraints(comp);
1605:
1606: if (oldComp == comp) {
1607: break;
1608: } else if (comp instanceof GridComponent) {
1609: GridComponent gc = (GridComponent) comp;
1610: Component bean = gc.getBeanChildComponent();
1611: if (bean instanceof JScrollPane) {
1612: JScrollPane scroll = (JScrollPane) bean;
1613: JViewport viewport = scroll.getViewport();
1614: if (viewport != null) {
1615: Component vbean = viewport.getView();
1616: if (vbean == oldComp) {
1617: comp = scroll;
1618: break;
1619: }
1620: }
1621: }
1622:
1623: if (bean == oldComp) {
1624: break;
1625: }
1626:
1627: } else if (comp instanceof JScrollPane) {
1628: JScrollPane scroll = (JScrollPane) comp;
1629: JViewport viewport = scroll.getViewport();
1630: if (viewport != null) {
1631: Component bean = viewport.getView();
1632: if (bean == oldComp) {
1633: break;
1634: }
1635: }
1636: }
1637: comp = null;
1638: }
1639:
1640: if (comp != null) {
1641: m_form.remove(comp);
1642: if (newComponent != null) {
1643: m_form.add(newComponent, cc);
1644: }
1645: refreshView();
1646: return comp;
1647: }
1648:
1649: System.err
1650: .println("replaceBean failed. Unable to find oldComp: "
1651: + oldComp.getClass()
1652: + " name: "
1653: + oldComp.getName());
1654: return null;
1655: }
1656:
1657: /**
1658: * FormAccessor implementation. Locates an existing bean with the given name
1659: * and replaces it with a new bean. If the existing bean cannot be found,
1660: * the request is ignored.
1661: *
1662: * @param compName
1663: * the name of the component to replace
1664: * @param newComponent
1665: * the component to add.
1666: * @return the component that was replaced. If this method fails for any
1667: * reason null is returned.
1668: */
1669: public Component replaceBean(String compName, Component newComponent) {
1670: if (compName == null) {
1671: System.err
1672: .println("replaceBean failed. compName cannot be null. ");
1673: return null;
1674: }
1675:
1676: Iterator iter = beanIterator(false);
1677: while (iter.hasNext()) {
1678: Component comp = (Component) iter.next();
1679: if (compName.equals(comp.getName())) {
1680: return replaceBean(comp, newComponent);
1681: }
1682: }
1683: System.err
1684: .println("replaceBean failed. Unable to find compName: "
1685: + compName);
1686: return null;
1687: }
1688:
1689: /**
1690: * Runs unit test routines on this View. Internally used method.
1691: */
1692: private void unitTest() {
1693: // JETATestFactory.runTest(
1694: // "test.jeta.forms.gui.form.GridViewValidator", this );
1695: }
1696:
1697: /**
1698: * Updates the local column groups reference so that it is consistent with
1699: * the form layout. The column groups are stored in a FormGroupSet variable.
1700: * This variable is updated to correspond to the column groups in the
1701: * FormLayout.
1702: */
1703: public void updateColumnGroups() {
1704: try {
1705: FormGroupSet gset = new FormGroupSet();
1706: int[][] groups = m_formlayout.getColumnGroups();
1707: for (int gindex = 0; gindex < groups.length; gindex++) {
1708: int[] cols = groups[gindex];
1709: if (cols != null) {
1710: Integer ckey = new Integer(gindex + 1);
1711: for (int cindex = 0; cindex < cols.length; cindex++) {
1712: gset.assignToGroup(ckey, cols[cindex]);
1713: }
1714: }
1715: }
1716: m_col_groups = gset;
1717: } catch (Exception e) {
1718: FormsLogger.debug(e);
1719: }
1720: }
1721:
1722: /**
1723: * Updates the local row groups reference so that it is consistent with the
1724: * form layout. The row groups are stored in a FormGroupSet variable. This
1725: * variable is updated to correspond to the row groups in the FormLayout.
1726: */
1727: public void updateRowGroups() {
1728: try {
1729: FormGroupSet gset = new FormGroupSet();
1730: int[][] groups = m_formlayout.getRowGroups();
1731: for (int gindex = 0; gindex < groups.length; gindex++) {
1732: int[] rows = groups[gindex];
1733: if (rows != null) {
1734: Integer rkey = new Integer(gindex + 1);
1735: for (int rindex = 0; rindex < rows.length; rindex++) {
1736: gset.assignToGroup(rkey, rows[rindex]);
1737: }
1738: }
1739: }
1740: m_row_groups = gset;
1741: } catch (Exception e) {
1742: FormsLogger.debug(e);
1743: }
1744: }
1745:
1746: /**
1747: * Traverses the container hierarhcy of a given container and notifies all
1748: * containers that are GridView instances that a nested form has changed.
1749: *
1750: * @param c
1751: * the container whose parent containment hiearchy to traverse
1752: * @param nestedModStamp
1753: * a time stamp indicating that a child component has changed.
1754: * This is needed to support fail-fast component iterators.
1755: */
1756: private void updateParentModicationStamps(Container c,
1757: long nestedModStamp) {
1758: while (c != null) {
1759: if (c instanceof GridView) {
1760: GridView gv = (GridView) c;
1761: gv.m_nested_mod_stamp = nestedModStamp;
1762: }
1763: c = c.getParent();
1764: if (c instanceof java.awt.Window
1765: || c instanceof javax.swing.JInternalFrame)
1766: break;
1767: }
1768: }
1769:
1770: /**
1771: * Used for iterating over the GridComponents in this view. This iterator
1772: * accesses all child components of the form container. Only components that
1773: * are GridComponent instances are returned by the iterator.
1774: */
1775: private class ComponentIterator implements Iterator {
1776: private int m_pos = -1;
1777:
1778: public boolean hasNext() {
1779: int pos = m_pos + 1;
1780: while (pos < m_form.getComponentCount()) {
1781: Object obj = m_form.getComponent(pos);
1782: if (obj instanceof GridComponent) {
1783: m_pos = pos - 1;
1784: return true;
1785: }
1786: pos++;
1787: }
1788: return false;
1789: }
1790:
1791: public Object next() {
1792: m_pos++;
1793: while (m_pos < m_form.getComponentCount()) {
1794: Object obj = m_form.getComponent(m_pos);
1795: if (obj instanceof GridComponent)
1796: return obj;
1797:
1798: }
1799: throw new java.util.NoSuchElementException();
1800: }
1801:
1802: public void remove() {
1803: throw new UnsupportedOperationException();
1804: }
1805: }
1806:
1807: /**
1808: * This is the container that has the actual FormLayout. This class
1809: * overrides JPanel mainly to intercept add/remove calls so the timestamp
1810: * can be modified. We keep track of the timestamp to support fail-fast
1811: * FormIterators.
1812: */
1813: private class FormContainer extends JPanel {
1814: /**
1815: * A timestamp of the last time this container was modified. This is
1816: * primarily used to implement the fail-fast FormIterator
1817: */
1818: private long m_mod_stamp;
1819:
1820: protected void addImpl(Component comp, Object constraints,
1821: int index) {
1822: super .addImpl(comp, constraints, index);
1823: m_mod_stamp = System.currentTimeMillis();
1824: updateParentModicationStamps(GridView.this .getParent(),
1825: m_mod_stamp);
1826: }
1827:
1828: public void remove(int index) {
1829: super .remove(index);
1830: m_mod_stamp = System.currentTimeMillis();
1831: updateParentModicationStamps(GridView.this .getParent(),
1832: m_mod_stamp);
1833: }
1834:
1835: public void removeAll() {
1836: super .removeAll();
1837: m_mod_stamp = System.currentTimeMillis();
1838: updateParentModicationStamps(GridView.this .getParent(),
1839: m_mod_stamp);
1840: }
1841:
1842: /**
1843: * @return the timestamp of the last time this container was modified.
1844: * This is primarily used to implement the fail-fast
1845: * FormIterator
1846: */
1847: long getModificationStamp() {
1848: return m_mod_stamp;
1849: }
1850: }
1851:
1852: }
|