Source Code Cross Referenced for FormLayout.java in  » Swing-Library » jgoodies-forms » com » jgoodies » forms » layout » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » jgoodies forms » com.jgoodies.forms.layout 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
0003:         *
0004:         * Redistribution and use in source and binary forms, with or without 
0005:         * modification, 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 JGoodies Karsten Lentzsch nor the names of 
0015:         *    its contributors may be used to endorse or promote products derived 
0016:         *    from this software without 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, 
0020:         * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
0021:         * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
0022:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
0023:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
0024:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
0025:         * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
0026:         * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
0027:         * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
0028:         * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
0029:         */
0030:
0031:        package com.jgoodies.forms.layout;
0032:
0033:        import java.awt.Component;
0034:        import java.awt.Container;
0035:        import java.awt.Dimension;
0036:        import java.awt.Insets;
0037:        import java.awt.LayoutManager2;
0038:        import java.awt.Rectangle;
0039:        import java.io.IOException;
0040:        import java.io.ObjectOutputStream;
0041:        import java.io.Serializable;
0042:        import java.util.*;
0043:
0044:        /**
0045:         * FormLayout is a powerful, flexible and precise general purpose 
0046:         * layout manager. It aligns components vertically and horizontally in
0047:         * a dynamic rectangular grid of cells, with each component occupying one or
0048:         * more cells.
0049:         * A <a href="../../../../../whitepaper.pdf" target="secondary">whitepaper</a>
0050:         * about the FormLayout ships with the product documentation and is available 
0051:         * <a href="http://www.jgoodies.com/articles/forms.pdf">online</a>.<p>
0052:         * 
0053:         * To use FormLayout you first define the grid by specifying the
0054:         * columns and rows. In a second step you add components to the grid. You can
0055:         * specify columns and rows via human-readable String descriptions or via
0056:         * arrays of {@link ColumnSpec} and {@link RowSpec} instances.<p>
0057:         * 
0058:         * Each component managed by a FormLayout is associated with an instance of
0059:         * {@link CellConstraints}. The constraints object specifies where a component
0060:         * should be located on the form's grid and how the component should be
0061:         * positioned. In addition to its constraints object the
0062:         * <code>FormLayout</code> also considers each component's minimum and
0063:         * preferred sizes in order to determine a component's size.<p>
0064:         * 
0065:         * FormLayout has been designed to work with non-visual builders that help you
0066:         * specify the layout and fill the grid. For example, the 
0067:         * {@link com.jgoodies.forms.builder.ButtonBarBuilder} assists you in building button
0068:         * bars; it creates a standardized FormLayout and provides a minimal API that
0069:         * specializes in adding buttons. Other builders can create frequently used
0070:         * panel design, for example a form that consists of rows of label-component
0071:         * pairs.<p>
0072:         *  
0073:         * FormLayout has been prepared to work with different types of sizes as
0074:         * defined by the {@link Size} interface.<p>
0075:         * 
0076:         * <strong>Example 1</strong> (Plain FormLayout):<br>
0077:         * The following example creates a panel with 3 data columns and 3 data rows; 
0078:         * the columns and rows are specified before components are added 
0079:         * to the form.
0080:         * <pre>
0081:         * FormLayout layout = new FormLayout(
0082:         *      "right:pref, 6dlu, 50dlu, 4dlu, default",  // columns 
0083:         *      "pref, 3dlu, pref, 3dlu, pref");           // rows
0084:         *
0085:         * CellConstraints cc = new CellConstraints();
0086:         * JPanel panel = new JPanel(layout);
0087:         * panel.add(new JLabel("Label1"),   cc.xy  (1, 1));
0088:         * panel.add(new JTextField(),       cc.xywh(3, 1, 3, 1));
0089:         * panel.add(new JLabel("Label2"),   cc.xy  (1, 3));
0090:         * panel.add(new JTextField(),       cc.xy  (3, 3));
0091:         * panel.add(new JLabel("Label3"),   cc.xy  (1, 5));
0092:         * panel.add(new JTextField(),       cc.xy  (3, 5));
0093:         * panel.add(new JButton("/u2026"),  cc.xy  (5, 5));
0094:         * return panel;
0095:         * </pre><p>
0096:         * 
0097:         * <strong>Example 2</strong> (Using PanelBuilder):<br>
0098:         * This example creates the same panel as above using the 
0099:         * {@link com.jgoodies.forms.builder.PanelBuilder} to add components to the form. 
0100:         * <pre>
0101:         * FormLayout layout = new FormLayout(
0102:         *      "right:pref, 6dlu, 50dlu, 4dlu, default",  // columns 
0103:         *      "pref, 3dlu, pref, 3dlu, pref");           // rows
0104:         *
0105:         * PanelBuilder builder = new PanelBuilder(layout);
0106:         * CellConstraints cc = new CellConstraints();
0107:         * builder.addLabel("Label1",         cc.xy  (1, 1));
0108:         * builder.add(new JTextField(),      cc.xywh(3, 1, 3, 1));
0109:         * builder.addLabel("Label2",         cc.xy  (1, 3));
0110:         * builder.add(new JTextField(),      cc.xy  (3, 3));
0111:         * builder.addLabel("Label3",         cc.xy  (1, 5));
0112:         * builder.add(new JTextField(),      cc.xy  (3, 5));
0113:         * builder.add(new JButton("/u2026"), cc.xy  (5, 5));
0114:         * return builder.getPanel();
0115:         * </pre><p>
0116:         * 
0117:         * <strong>Example 3</strong> (Using DefaultFormBuilder):<br>
0118:         * This example utilizes the 
0119:         * {@link com.jgoodies.forms.builder.DefaultFormBuilder} that 
0120:         * ships with the source distribution. 
0121:         * <pre>
0122:         * FormLayout layout = new FormLayout(
0123:         *      "right:pref, 6dlu, 50dlu, 4dlu, default"); // 5 columns; add rows later
0124:         *
0125:         * DefaultFormBuilder builder = new DefaultFormBuilder(layout);
0126:         * builder.append("Label1", new JTextField(), 3);
0127:         * builder.append("Label2", new JTextField());
0128:         * builder.append("Label3", new JTextField());
0129:         * builder.append(new JButton("/u2026"));
0130:         * return builder.getPanel();
0131:         * </pre><p>
0132:         * 
0133:         * TODO: In the Forms 1.0.x invisible components are not taken into account
0134:         * when the FormLayout lays out the container. Add an optional setting for
0135:         * this on both the container-level and component-level. So one can specify
0136:         * that invisible components shall be taken into account, but may exclude
0137:         * individual components. Or the other way round, exclude invisible components,
0138:         * and include individual components. The API of both the FormLayout and
0139:         * CellConstraints classes shall be extended to support this option.
0140:         * This feature is planned for the Forms version 1.1 and is described in 
0141:         * <a href="https://forms.dev.java.net/issues/show_bug.cgi?id=28">issue #28</a> 
0142:         * of the Forms' issue tracker where you can track the progress.
0143:         * 
0144:         * @author Karsten Lentzsch
0145:         * @version $Revision: 1.6 $
0146:         * 
0147:         * @see	ColumnSpec
0148:         * @see	RowSpec
0149:         * @see	CellConstraints
0150:         * @see	com.jgoodies.forms.builder.AbstractFormBuilder
0151:         * @see	com.jgoodies.forms.builder.ButtonBarBuilder
0152:         * @see	com.jgoodies.forms.builder.DefaultFormBuilder
0153:         * @see	com.jgoodies.forms.factories.FormFactory
0154:         * @see	Size
0155:         * @see	Sizes
0156:         */
0157:
0158:        public final class FormLayout implements  LayoutManager2, Serializable {
0159:
0160:            /**
0161:             * Holds the column specifications.
0162:             * 
0163:             * @see ColumnSpec
0164:             * @see #getColumnCount()
0165:             * @see #getColumnSpec(int)
0166:             * @see #appendColumn(ColumnSpec)
0167:             * @see #insertColumn(int, ColumnSpec)
0168:             * @see #removeColumn(int)
0169:             */
0170:            private final List colSpecs;
0171:
0172:            /**
0173:             * Holds the row specifications.
0174:             * 
0175:             * @see RowSpec
0176:             * @see #getRowCount()
0177:             * @see #getRowSpec(int)
0178:             * @see #appendRow(RowSpec)
0179:             * @see #insertRow(int, RowSpec)
0180:             * @see #removeRow(int)
0181:             */
0182:            private final List rowSpecs;
0183:
0184:            /**
0185:             * Holds the column groups as an array of arrays of column indices.
0186:             * 
0187:             * @see #getColumnGroups()
0188:             * @see #setColumnGroups(int[][])
0189:             * @see #addGroupedColumn(int)
0190:             */
0191:            private int[][] colGroupIndices;
0192:
0193:            /**
0194:             * Holds the row groups as an array of arrays of row indices.
0195:             * 
0196:             * @see #getRowGroups()
0197:             * @see #setRowGroups(int[][])
0198:             * @see #addGroupedRow(int)
0199:             */
0200:            private int[][] rowGroupIndices;
0201:
0202:            /**
0203:             * Maps components to their associated <code>CellConstraints</code>.
0204:             * 
0205:             * @see CellConstraints
0206:             * @see #getConstraints(Component)
0207:             * @see #setConstraints(Component, CellConstraints)
0208:             */
0209:            private final Map constraintMap;
0210:
0211:            // Fields used by the Layout Algorithm **********************************
0212:
0213:            /**
0214:             * Holds the components that occupy exactly one column. 
0215:             * For each column we keep a list of these components.
0216:             */
0217:            private transient List[] colComponents;
0218:
0219:            /**
0220:             * Holds the components that occupy exactly one row. 
0221:             * For each row we keep a list of these components.
0222:             */
0223:            private transient List[] rowComponents;
0224:
0225:            /**
0226:             * Caches component minimum and preferred sizes.
0227:             * All requests for component sizes shall be directed to the cache.
0228:             */
0229:            private final ComponentSizeCache componentSizeCache;
0230:
0231:            /**
0232:             * These functional objects are used to measure component sizes.
0233:             * They abstract from horizontal and vertical orientation and so,
0234:             * allow to implement the layout algorithm for both orientations with a
0235:             * single set of methods.
0236:             */
0237:            private final Measure minimumWidthMeasure;
0238:            private final Measure minimumHeightMeasure;
0239:            private final Measure preferredWidthMeasure;
0240:            private final Measure preferredHeightMeasure;
0241:
0242:            // Instance Creation ****************************************************
0243:
0244:            /**
0245:             * Constructs an empty FormLayout. Columns and rows must be added
0246:             * before components can be added to the layout container.<p>
0247:             * 
0248:             * This constructor is intended to be used in environments
0249:             * that add columns and rows dynamically.
0250:             */
0251:            public FormLayout() {
0252:                this (new ColumnSpec[0], new RowSpec[0]);
0253:            }
0254:
0255:            /**
0256:             * Constructs a FormLayout using the given encoded column specifications. 
0257:             * The constructed layout has no rows; these must be added 
0258:             * before components can be added to the layout container.<p>
0259:             * 
0260:             * This constructor is primarily intended to be used with builder classes
0261:             * that add rows dynamically, such as the <code>DefaultFormBuilder</code>.<p>
0262:             * 
0263:             * <strong>Examples:</strong><pre>
0264:             * // Label, gap, component
0265:             * FormLayout layout = new FormLayout(
0266:             *      "pref, 4dlu, pref");  
0267:             *                 
0268:             * // Right-aligned label, gap, component, gap, component                                         
0269:             * FormLayout layout = new FormLayout(
0270:             *      "right:pref, 4dlu, 50dlu, 4dlu, 50dlu");  
0271:             *      
0272:             * // Left-aligned labels, gap, components, gap, components                                         
0273:             * FormLayout layout = new FormLayout(
0274:             *      "left:pref, 4dlu, pref, 4dlu, pref"); 
0275:             * </pre> See the class comment for more examples.
0276:             * 
0277:             * @param encodedColumnSpecs  comma separated encoded column specifications
0278:             * @throws NullPointerException  if encodedColumnSpecs is <code>null</code>
0279:             */
0280:            public FormLayout(String encodedColumnSpecs) {
0281:                this (ColumnSpec.decodeSpecs(encodedColumnSpecs), new RowSpec[0]);
0282:            }
0283:
0284:            /**
0285:             * Constructs a FormLayout using the given 
0286:             * encoded column and row specifications.<p>
0287:             * 
0288:             * This constructor is recommended for most hand-coded layouts.<p>
0289:             * 
0290:             * <strong>Examples:</strong><pre>
0291:             * FormLayout layout = new FormLayout(
0292:             *      "pref, 4dlu, pref",               // columns 
0293:             *      "p, 3dlu, p");                    // rows
0294:             *                 
0295:             * FormLayout layout = new FormLayout(
0296:             *      "right:pref, 4dlu, pref",         // columns 
0297:             *      "p, 3dlu, p, 3dlu, fill:p:grow"); // rows
0298:             *      
0299:             * FormLayout layout = new FormLayout(
0300:             *      "left:pref, 4dlu, 50dlu",         // columns 
0301:             *      "p, 2px, p, 3dlu, p, 9dlu, p");   // rows
0302:             *      
0303:             * FormLayout layout = new FormLayout(
0304:             *      "max(75dlu;pref), 4dlu, default", // columns 
0305:             *      "p, 3dlu, p, 3dlu, p, 3dlu, p");  // rows
0306:             * </pre> See the class comment for more examples.
0307:             * 
0308:             * @param encodedColumnSpecs  comma separated encoded column specifications
0309:             * @param encodedRowSpecs     comma separated encoded row specifications
0310:             * @throws NullPointerException  if encodedColumnSpecs or encodedRowSpecs 
0311:             *     is <code>null</code>
0312:             */
0313:            public FormLayout(String encodedColumnSpecs, String encodedRowSpecs) {
0314:                this (ColumnSpec.decodeSpecs(encodedColumnSpecs), RowSpec
0315:                        .decodeSpecs(encodedRowSpecs));
0316:            }
0317:
0318:            /**
0319:             * Constructs a FormLayout using the given column specifications.
0320:             * The constructed layout has no rows; these must be added 
0321:             * before components can be added to the layout container.
0322:             * 
0323:             * @param colSpecs  an array of column specifications.
0324:             * @throws NullPointerException if colSpecs is null
0325:             * 
0326:             * @since 1.1
0327:             */
0328:            public FormLayout(ColumnSpec[] colSpecs) {
0329:                this (colSpecs, new RowSpec[] {});
0330:            }
0331:
0332:            /**
0333:             * Constructs a FormLayout using the given column and row specifications.
0334:             * 
0335:             * @param colSpecs	an array of column specifications.
0336:             * @param rowSpecs	an array of row specifications.
0337:             * @throws NullPointerException if colSpecs or rowSpecs is null
0338:             */
0339:            public FormLayout(ColumnSpec[] colSpecs, RowSpec[] rowSpecs) {
0340:                if (colSpecs == null)
0341:                    throw new NullPointerException(
0342:                            "The column specifications must not be null.");
0343:                if (rowSpecs == null)
0344:                    throw new NullPointerException(
0345:                            "The row specifications must not be null.");
0346:
0347:                this .colSpecs = new ArrayList(Arrays.asList(colSpecs));
0348:                this .rowSpecs = new ArrayList(Arrays.asList(rowSpecs));
0349:                colGroupIndices = new int[][] {};
0350:                rowGroupIndices = new int[][] {};
0351:                int initialCapacity = colSpecs.length * rowSpecs.length / 4;
0352:                constraintMap = new HashMap(initialCapacity);
0353:                componentSizeCache = new ComponentSizeCache(initialCapacity);
0354:                minimumWidthMeasure = new MinimumWidthMeasure(
0355:                        componentSizeCache);
0356:                minimumHeightMeasure = new MinimumHeightMeasure(
0357:                        componentSizeCache);
0358:                preferredWidthMeasure = new PreferredWidthMeasure(
0359:                        componentSizeCache);
0360:                preferredHeightMeasure = new PreferredHeightMeasure(
0361:                        componentSizeCache);
0362:            }
0363:
0364:            // Accessing the Column and Row Specifications **************************
0365:
0366:            /**
0367:             * Returns the number of columns in this layout.
0368:             * 
0369:             * @return the number of columns
0370:             */
0371:            public int getColumnCount() {
0372:                return colSpecs.size();
0373:            }
0374:
0375:            /**
0376:             * Returns the number of rows in this layout.
0377:             * 
0378:             * @return the number of rows
0379:             */
0380:            public int getRowCount() {
0381:                return rowSpecs.size();
0382:            }
0383:
0384:            /**
0385:             * Returns the <code>ColumnSpec</code> at the specified column index.
0386:             * 
0387:             * @param columnIndex   the column index of the requested <code>ColumnSpec</code>
0388:             * @return the <code>ColumnSpec</code> at the specified column
0389:             * @throws IndexOutOfBoundsException if the column index is out of range
0390:             */
0391:            public ColumnSpec getColumnSpec(int columnIndex) {
0392:                return (ColumnSpec) colSpecs.get(columnIndex - 1);
0393:            }
0394:
0395:            /**
0396:             * Sets the <code>ColumnSpec</code> at the specified column index.
0397:             * 
0398:             * @param columnIndex   the index of the column to be changed
0399:             * @param columnSpec    the <code>ColumnSpec</code> to be set
0400:             * @throws NullPointerException if the column specification is null
0401:             * @throws IndexOutOfBoundsException if the column index is out of range
0402:             */
0403:            public void setColumnSpec(int columnIndex, ColumnSpec columnSpec) {
0404:                if (columnSpec == null) {
0405:                    throw new NullPointerException(
0406:                            "The column spec must not be null.");
0407:                }
0408:                colSpecs.set(columnIndex - 1, columnSpec);
0409:            }
0410:
0411:            /**
0412:             * Returns the <code>RowSpec</code> at the specified row index.
0413:             * 
0414:             * @param rowIndex   the row index of the requested <code>RowSpec</code>
0415:             * @return the <code>RowSpec</code> at the specified row
0416:             * @throws IndexOutOfBoundsException if the row index is out of range
0417:             */
0418:            public RowSpec getRowSpec(int rowIndex) {
0419:                return (RowSpec) rowSpecs.get(rowIndex - 1);
0420:            }
0421:
0422:            /**
0423:             * Sets the <code>RowSpec</code> at the specified row index.
0424:             * 
0425:             * @param rowIndex   the index of the row to be changed
0426:             * @param rowSpec    the <code>RowSpec</code> to be set
0427:             * @throws NullPointerException if the row specification is null
0428:             * @throws IndexOutOfBoundsException if the row index is out of range
0429:             */
0430:            public void setRowSpec(int rowIndex, RowSpec rowSpec) {
0431:                if (rowSpec == null) {
0432:                    throw new NullPointerException(
0433:                            "The row spec must not be null.");
0434:                }
0435:                rowSpecs.set(rowIndex - 1, rowSpec);
0436:            }
0437:
0438:            /**
0439:             * Appends the given column specification to the right hand side of all
0440:             * columns.
0441:             * 
0442:             * @param columnSpec the column specification to be added 
0443:             * @throws NullPointerException if the column specification is null
0444:             */
0445:            public void appendColumn(ColumnSpec columnSpec) {
0446:                if (columnSpec == null) {
0447:                    throw new NullPointerException(
0448:                            "The column spec must not be null.");
0449:                }
0450:                colSpecs.add(columnSpec);
0451:            }
0452:
0453:            /**
0454:             * Inserts the specified column at the specified position. Shifts components 
0455:             * that intersect the new column to the right hand side and readjusts
0456:             * column groups.<p>
0457:             *  
0458:             * The component shift works as follows: components that were located on
0459:             * the right hand side of the inserted column are shifted one column to
0460:             * the right; component column span is increased by one if it intersects
0461:             * the new column.<p>
0462:             * 
0463:             * Column group indices that are greater or equal than the given column
0464:             * index will be increased by one.
0465:             * 
0466:             * @param columnIndex  index of the column to be inserted
0467:             * @param columnSpec   specification of the column to be inserted
0468:             * @throws IndexOutOfBoundsException if the column index is out of range
0469:             */
0470:            public void insertColumn(int columnIndex, ColumnSpec columnSpec) {
0471:                if (columnIndex < 1 || columnIndex > getColumnCount()) {
0472:                    throw new IndexOutOfBoundsException("The column index "
0473:                            + columnIndex + "must be in the range [1, "
0474:                            + getColumnCount() + "].");
0475:                }
0476:                colSpecs.add(columnIndex - 1, columnSpec);
0477:                shiftComponentsHorizontally(columnIndex, false);
0478:                adjustGroupIndices(colGroupIndices, columnIndex, false);
0479:            }
0480:
0481:            /**
0482:             * Removes the column with the given column index from the layout.
0483:             * Components will be rearranged and column groups will be readjusted.
0484:             * Therefore, the column must not contain components and must not be part
0485:             * of a column group.<p>
0486:             * 
0487:             * The component shift works as follows: components that were located on
0488:             * the right hand side of the removed column are moved one column to the
0489:             * left; component column span is decreased by one if it intersects the
0490:             * removed column.<p>
0491:             * 
0492:             * Column group indices that are greater than the column index will be
0493:             * decreased by one.<p>
0494:             * 
0495:             * <strong>Note:</strong> If one of the constraints mentioned above 
0496:             * is violated, this layout's state becomes illegal and it is unsafe 
0497:             * to work with this layout.
0498:             * A typical layout implementation can ensure that these constraints are
0499:             * not violated. However, in some cases you may need to check these
0500:             * conditions before you invoke this method. The Forms extras contain
0501:             * source code for class <code>FormLayoutUtils</code> that provides
0502:             * the required test methods:<br> 
0503:             * <code>#columnContainsComponents(Container, int)</code> and<br>
0504:             * <code>#isGroupedColumn(FormLayout, int)</code>.
0505:             * 
0506:             * @param columnIndex  index of the column to remove
0507:             * @throws IndexOutOfBoundsException if the column index is out of range
0508:             * @throws IllegalStateException  if the column contains components
0509:             *     or if the column is already grouped
0510:             * 
0511:             * @see com.jgoodies.forms.extras.FormLayoutUtils#columnContainsComponent(Container, int)
0512:             * @see com.jgoodies.forms.extras.FormLayoutUtils#isGroupedColumn(FormLayout, int)
0513:             */
0514:            public void removeColumn(int columnIndex) {
0515:                if (columnIndex < 1 || columnIndex > getColumnCount()) {
0516:                    throw new IndexOutOfBoundsException("The column index "
0517:                            + columnIndex + " must be in the range [1, "
0518:                            + getColumnCount() + "].");
0519:                }
0520:                colSpecs.remove(columnIndex - 1);
0521:                shiftComponentsHorizontally(columnIndex, true);
0522:                adjustGroupIndices(colGroupIndices, columnIndex, true);
0523:            }
0524:
0525:            /**
0526:             * Appends the given row specification to the bottom of all rows.
0527:             * 
0528:             * @param rowSpec  the row specification to be added to the form layout
0529:             * @throws NullPointerException if the rowSpec is null
0530:             */
0531:            public void appendRow(RowSpec rowSpec) {
0532:                if (rowSpec == null) {
0533:                    throw new NullPointerException(
0534:                            "The row spec must not be null.");
0535:                }
0536:                rowSpecs.add(rowSpec);
0537:            }
0538:
0539:            /**
0540:             * Inserts the specified column at the specified position. Shifts
0541:             * components that intersect the new column to the right and readjusts
0542:             * column groups.<p>
0543:             *  
0544:             * The component shift works as follows: components that were located on
0545:             * the right hand side of the inserted column are shifted one column to
0546:             * the right; component column span is increased by one if it intersects
0547:             * the new column.<p>
0548:             * 
0549:             * Column group indices that are greater or equal than the given column
0550:             * index will be increased by one.
0551:             * 
0552:             * @param rowIndex  index of the row to be inserted
0553:             * @param rowSpec   specification of the row to be inserted
0554:             * @throws IndexOutOfBoundsException if the row index is out of range
0555:             */
0556:            public void insertRow(int rowIndex, RowSpec rowSpec) {
0557:                if (rowIndex < 1 || rowIndex > getRowCount()) {
0558:                    throw new IndexOutOfBoundsException("The row index "
0559:                            + rowIndex + " must be in the range [1, "
0560:                            + getRowCount() + "].");
0561:                }
0562:                rowSpecs.add(rowIndex - 1, rowSpec);
0563:                shiftComponentsVertically(rowIndex, false);
0564:                adjustGroupIndices(rowGroupIndices, rowIndex, false);
0565:            }
0566:
0567:            /**
0568:             * Removes the row with the given row index from the layout. Components
0569:             * will be rearranged and row groups will be readjusted. Therefore, the
0570:             * row must not contain components and must not be part of a row group.<p>
0571:             * 
0572:             * The component shift works as follows: components that were located
0573:             * below the removed row are moved up one row; component row span is
0574:             * decreased by one if it intersects the removed row.<p>
0575:             * 
0576:             * Row group indices that are greater than the row index will be decreased
0577:             * by one.<p>
0578:             * 
0579:             * <strong>Note:</strong> If one of the constraints mentioned above 
0580:             * is violated, this layout's state becomes illegal and it is unsafe 
0581:             * to work with this layout.
0582:             * A typical layout implementation can ensure that these constraints are
0583:             * not violated. However, in some cases you may need to check these
0584:             * conditions before you invoke this method. The Forms extras contain
0585:             * source code for class <code>FormLayoutUtils</code> that provides
0586:             * the required test methods:<br> 
0587:             * <code>#rowContainsComponents(Container, int)</code> and<br>
0588:             * <code>#isGroupedRow(FormLayout, int)</code>.
0589:             *  
0590:             * @param rowIndex  index of the row to remove
0591:             * @throws IndexOutOfBoundsException if the row index is out of range
0592:             * @throws IllegalStateException if the row contains components
0593:             *     or if the row is already grouped
0594:             * 
0595:             * @see com.jgoodies.forms.extras.FormLayoutUtils#rowContainsComponent(Container, int)
0596:             * @see com.jgoodies.forms.extras.FormLayoutUtils#isGroupedRow(FormLayout, int)
0597:             */
0598:            public void removeRow(int rowIndex) {
0599:                if (rowIndex < 1 || rowIndex > getRowCount()) {
0600:                    throw new IndexOutOfBoundsException("The row index "
0601:                            + rowIndex + "must be in the range [1, "
0602:                            + getRowCount() + "].");
0603:                }
0604:                rowSpecs.remove(rowIndex - 1);
0605:                shiftComponentsVertically(rowIndex, true);
0606:                adjustGroupIndices(rowGroupIndices, rowIndex, true);
0607:            }
0608:
0609:            /**
0610:             * Shifts components horizontally, either to the right if a column has been
0611:             * inserted or to the left if a column has been removed.
0612:             * 
0613:             * @param columnIndex  index of the column to remove
0614:             * @param remove  		true for remove, false for insert
0615:             * @throws IllegalStateException if a removed column contains components
0616:             */
0617:            private void shiftComponentsHorizontally(int columnIndex,
0618:                    boolean remove) {
0619:                final int offset = remove ? -1 : 1;
0620:                for (Iterator i = constraintMap.entrySet().iterator(); i
0621:                        .hasNext();) {
0622:                    Map.Entry entry = (Map.Entry) i.next();
0623:                    CellConstraints constraints = (CellConstraints) entry
0624:                            .getValue();
0625:                    int x1 = constraints.gridX;
0626:                    int w = constraints.gridWidth;
0627:                    int x2 = x1 + w - 1;
0628:                    if (x1 == columnIndex && remove) {
0629:                        throw new IllegalStateException("The removed column "
0630:                                + columnIndex
0631:                                + " must not contain component origins.\n"
0632:                                + "Illegal component=" + entry.getKey());
0633:                    } else if (x1 >= columnIndex) {
0634:                        constraints.gridX += offset;
0635:                    } else if (x2 >= columnIndex) {
0636:                        constraints.gridWidth += offset;
0637:                    }
0638:                }
0639:            }
0640:
0641:            /**
0642:             * Shifts components vertically, either to the bottom if a row has been
0643:             * inserted or to the top if a row has been removed.
0644:             * 
0645:             * @param rowIndex      index of the row to remove
0646:             * @param remove        true for remove, false for insert
0647:             * @throws IllegalStateException if a removed column contains components
0648:             */
0649:            private void shiftComponentsVertically(int rowIndex, boolean remove) {
0650:                final int offset = remove ? -1 : 1;
0651:                for (Iterator i = constraintMap.entrySet().iterator(); i
0652:                        .hasNext();) {
0653:                    Map.Entry entry = (Map.Entry) i.next();
0654:                    CellConstraints constraints = (CellConstraints) entry
0655:                            .getValue();
0656:                    int y1 = constraints.gridY;
0657:                    int h = constraints.gridHeight;
0658:                    int y2 = y1 + h - 1;
0659:                    if (y1 == rowIndex && remove) {
0660:                        throw new IllegalStateException("The removed row "
0661:                                + rowIndex
0662:                                + " must not contain component origins.\n"
0663:                                + "Illegal component=" + entry.getKey());
0664:                    } else if (y1 >= rowIndex) {
0665:                        constraints.gridY += offset;
0666:                    } else if (y2 >= rowIndex) {
0667:                        constraints.gridHeight += offset;
0668:                    }
0669:                }
0670:            }
0671:
0672:            /**
0673:             * Adjusts group indices. Shifts the given groups to left, right, up,
0674:             * down according to the specified remove or add flag.
0675:             * 
0676:             * @param allGroupIndices   the groups to be adjusted
0677:             * @param modifiedIndex     the modified column or row index
0678:             * @param remove			true for remove, false for add
0679:             * @throws IllegalStateException if we remove and the index is grouped
0680:             */
0681:            private void adjustGroupIndices(int[][] allGroupIndices,
0682:                    int modifiedIndex, boolean remove) {
0683:                final int offset = remove ? -1 : +1;
0684:                for (int group = 0; group < allGroupIndices.length; group++) {
0685:                    int[] groupIndices = allGroupIndices[group];
0686:                    for (int i = 0; i < groupIndices.length; i++) {
0687:                        int index = groupIndices[i];
0688:                        if (index == modifiedIndex && remove) {
0689:                            throw new IllegalStateException(
0690:                                    "The removed index " + modifiedIndex
0691:                                            + " must not be grouped.");
0692:                        } else if (index >= modifiedIndex) {
0693:                            groupIndices[i] += offset;
0694:                        }
0695:                    }
0696:                }
0697:            }
0698:
0699:            // Accessing Constraints ************************************************
0700:
0701:            /**
0702:             * Looks up and returns the constraints for the specified component. 
0703:             * A copy of the actual <code>CellConstraints</code> object is returned.
0704:             * 
0705:             * @param component    the component to be queried
0706:             * @return the <code>CellConstraints</code> for the specified component
0707:             * @throws NullPointerException if component is <code>null</code> or 
0708:             *     has not been added to the container
0709:             */
0710:            public CellConstraints getConstraints(Component component) {
0711:                if (component == null)
0712:                    throw new NullPointerException(
0713:                            "The component must not be null.");
0714:
0715:                CellConstraints constraints = (CellConstraints) constraintMap
0716:                        .get(component);
0717:                if (constraints == null)
0718:                    throw new NullPointerException(
0719:                            "The component has not been added to the container.");
0720:
0721:                return (CellConstraints) constraints.clone();
0722:            }
0723:
0724:            /**
0725:             * Sets the constraints for the specified component in this layout.
0726:             * 
0727:             * @param component     the component to be modified
0728:             * @param constraints   the constraints to be applied
0729:             * @throws NullPointerException   if the component or constraints object
0730:             *     is <code>null</code>
0731:             */
0732:            public void setConstraints(Component component,
0733:                    CellConstraints constraints) {
0734:                if (component == null)
0735:                    throw new NullPointerException(
0736:                            "The component must not be null.");
0737:                if (constraints == null)
0738:                    throw new NullPointerException(
0739:                            "The constraints must not be null.");
0740:
0741:                constraints.ensureValidGridBounds(getColumnCount(),
0742:                        getRowCount());
0743:                constraintMap.put(component, constraints.clone());
0744:            }
0745:
0746:            /**
0747:             * Removes the constraints for the specified component in this layout.
0748:             * 
0749:             * @param component  the component to be modified
0750:             */
0751:            private void removeConstraints(Component component) {
0752:                constraintMap.remove(component);
0753:                componentSizeCache.removeEntry(component);
0754:            }
0755:
0756:            // Accessing Column and Row Groups **************************************
0757:
0758:            /**
0759:             * Returns a deep copy of the column groups.
0760:             * 
0761:             * @return the column groups as two-dimensional int array
0762:             */
0763:            public int[][] getColumnGroups() {
0764:                return deepClone(colGroupIndices);
0765:            }
0766:
0767:            /**
0768:             * Sets the column groups, where each column in a group gets the same
0769:             * group wide width. Each group is described by an array of integers that
0770:             * are interpreted as column indices. The parameter is an array of such
0771:             * group descriptions.<p>
0772:             * 
0773:             * <strong>Examples:</strong><pre>
0774:             * // Group columns 1, 3 and 4. 
0775:             * setColumnGroups(new int[][]{ {1, 3, 4}});
0776:             * 
0777:             * // Group columns 1, 3, 4, and group columns 7 and 9
0778:             * setColumnGroups(new int[][]{ {1, 3, 4}, {7, 9}});
0779:             * </pre>
0780:             * 
0781:             * @param colGroupIndices	a two-dimensional array of column groups indices
0782:             * @throws	IndexOutOfBoundsException if an index is outside the grid
0783:             * @throws IllegalArgumentException if a column index is used twice
0784:             */
0785:            public void setColumnGroups(int[][] colGroupIndices) {
0786:                int maxColumn = getColumnCount();
0787:                boolean[] usedIndices = new boolean[maxColumn + 1];
0788:                for (int group = 0; group < colGroupIndices.length; group++) {
0789:                    for (int j = 0; j < colGroupIndices[group].length; j++) {
0790:                        int colIndex = colGroupIndices[group][j];
0791:                        if (colIndex < 1 || colIndex > maxColumn) {
0792:                            throw new IndexOutOfBoundsException(
0793:                                    "Invalid column group index " + colIndex
0794:                                            + " in group " + (group + 1));
0795:                        }
0796:                        if (usedIndices[colIndex]) {
0797:                            throw new IllegalArgumentException(
0798:                                    "Column index "
0799:                                            + colIndex
0800:                                            + " must not be used in multiple column groups.");
0801:                        }
0802:                        usedIndices[colIndex] = true;
0803:                    }
0804:                }
0805:                this .colGroupIndices = deepClone(colGroupIndices);
0806:            }
0807:
0808:            /**
0809:             * Adds the specified column index to the last column group. 
0810:             * In case there are no groups, a new group will be created.
0811:             * 
0812:             * @param columnIndex	the column index to be set grouped
0813:             */
0814:            public void addGroupedColumn(int columnIndex) {
0815:                int[][] newColGroups = getColumnGroups();
0816:                // Create a group if none exists.
0817:                if (newColGroups.length == 0) {
0818:                    newColGroups = new int[][] { { columnIndex } };
0819:                } else {
0820:                    int lastGroupIndex = newColGroups.length - 1;
0821:                    int[] lastGroup = newColGroups[lastGroupIndex];
0822:                    int groupSize = lastGroup.length;
0823:                    int[] newLastGroup = new int[groupSize + 1];
0824:                    System.arraycopy(lastGroup, 0, newLastGroup, 0, groupSize);
0825:                    newLastGroup[groupSize] = columnIndex;
0826:                    newColGroups[lastGroupIndex] = newLastGroup;
0827:                }
0828:                setColumnGroups(newColGroups);
0829:            }
0830:
0831:            /**
0832:             * Returns a deep copy of the row groups.
0833:             * 
0834:             * @return the row groups as two-dimensional int array
0835:             */
0836:            public int[][] getRowGroups() {
0837:                return deepClone(rowGroupIndices);
0838:            }
0839:
0840:            /**
0841:             * Sets the row groups, where each row in such a group gets the same group
0842:             * wide height. Each group is described by an array of integers that are
0843:             * interpreted as row indices. The parameter is an array of such group
0844:             * descriptions.<p>
0845:             * 
0846:             * <strong>Examples:</strong><pre>
0847:             * // Group rows 1 and 2.
0848:             * setRowGroups(new int[][]{ {1, 2}});
0849:             * 
0850:             * // Group rows 1 and 2, and group rows 5, 7, and 9.
0851:             * setRowGroups(new int[][]{ {1, 2}, {5, 7, 9}});
0852:             * </pre>
0853:             * 
0854:             * @param rowGroupIndices a two-dimensional array of row group indices.
0855:             * @throws IndexOutOfBoundsException if an index is outside the grid
0856:             */
0857:            public void setRowGroups(int[][] rowGroupIndices) {
0858:                int rowCount = getRowCount();
0859:                boolean[] usedIndices = new boolean[rowCount + 1];
0860:                for (int i = 0; i < rowGroupIndices.length; i++) {
0861:                    for (int j = 0; j < rowGroupIndices[i].length; j++) {
0862:                        int rowIndex = rowGroupIndices[i][j];
0863:                        if (rowIndex < 1 || rowIndex > rowCount) {
0864:                            throw new IndexOutOfBoundsException(
0865:                                    "Invalid row group index " + rowIndex
0866:                                            + " in group " + (i + 1));
0867:                        }
0868:                        if (usedIndices[rowIndex]) {
0869:                            throw new IllegalArgumentException(
0870:                                    "Row index "
0871:                                            + rowIndex
0872:                                            + " must not be used in multiple row groups.");
0873:                        }
0874:                        usedIndices[rowIndex] = true;
0875:                    }
0876:                }
0877:                this .rowGroupIndices = deepClone(rowGroupIndices);
0878:            }
0879:
0880:            /**
0881:             * Adds the specified row index to the last row group. 
0882:             * In case there are no groups, a new group will be created.
0883:             * 
0884:             * @param rowIndex   the index of the row that should be grouped
0885:             */
0886:            public void addGroupedRow(int rowIndex) {
0887:                int[][] newRowGroups = getRowGroups();
0888:                // Create a group if none exists.
0889:                if (newRowGroups.length == 0) {
0890:                    newRowGroups = new int[][] { { rowIndex } };
0891:                } else {
0892:                    int lastGroupIndex = newRowGroups.length - 1;
0893:                    int[] lastGroup = newRowGroups[lastGroupIndex];
0894:                    int groupSize = lastGroup.length;
0895:                    int[] newLastGroup = new int[groupSize + 1];
0896:                    System.arraycopy(lastGroup, 0, newLastGroup, 0, groupSize);
0897:                    newLastGroup[groupSize] = rowIndex;
0898:                    newRowGroups[lastGroupIndex] = newLastGroup;
0899:                }
0900:                setRowGroups(newRowGroups);
0901:            }
0902:
0903:            // Implementing the LayoutManager and LayoutManager2 Interfaces *********
0904:
0905:            /**
0906:             * Throws an <code>UnsupportedOperationException</code>. Does not add 
0907:             * the specified component with the specified name to the layout.
0908:             *
0909:             * @param name         indicates entry's position and anchor
0910:             * @param component    component to add
0911:             * @throws UnsupportedOperationException always
0912:             */
0913:            public void addLayoutComponent(String name, Component component) {
0914:                throw new UnsupportedOperationException(
0915:                        "Use #addLayoutComponent(Component, Object) instead.");
0916:            }
0917:
0918:            /**
0919:             * Adds the specified component to the layout, using the specified
0920:             * <code>constraints</code> object.  Note that constraints are mutable and
0921:             * are, therefore, cloned when cached.
0922:             *
0923:             * @param comp         the component to be added
0924:             * @param constraints  the component's cell constraints
0925:             * @throws NullPointerException if <code>constraints</code> is <code>null</code>
0926:             * @throws IllegalArgumentException if <code>constraints</code> is not a
0927:             * <code>CellConstraints</code> or a String that cannot be used to construct
0928:             * a <code>CellConstraints</code>
0929:             */
0930:            public void addLayoutComponent(Component comp, Object constraints) {
0931:                if (constraints instanceof  String) {
0932:                    setConstraints(comp, new CellConstraints(
0933:                            (String) constraints));
0934:                } else if (constraints instanceof  CellConstraints) {
0935:                    setConstraints(comp, (CellConstraints) constraints);
0936:                } else if (constraints == null) {
0937:                    throw new NullPointerException(
0938:                            "The constraints must not be null.");
0939:                } else {
0940:                    throw new IllegalArgumentException(
0941:                            "Illegal constraint type " + constraints.getClass());
0942:                }
0943:            }
0944:
0945:            /**
0946:             * Removes the specified component from this layout.<p>
0947:             * 
0948:             * Most applications do not call this method directly.
0949:             * 
0950:             * @param comp   the component to be removed.
0951:             * @see Container#remove(java.awt.Component)
0952:             * @see Container#removeAll()
0953:             */
0954:            public void removeLayoutComponent(Component comp) {
0955:                removeConstraints(comp);
0956:            }
0957:
0958:            // Layout Requests ******************************************************
0959:
0960:            /**
0961:             * Determines the minimum size of the <code>parent</code> container
0962:             * using this form layout.<p>
0963:             * 
0964:             * Most applications do not call this method directly.
0965:             * 
0966:             * @param parent   the container in which to do the layout
0967:             * @return the minimum size of the <code>parent</code> container
0968:             * 
0969:             * @see Container#doLayout()
0970:             */
0971:            public Dimension minimumLayoutSize(Container parent) {
0972:                return computeLayoutSize(parent, minimumWidthMeasure,
0973:                        minimumHeightMeasure);
0974:            }
0975:
0976:            /**
0977:             * Determines the preferred size of the <code>parent</code>
0978:             * container using this form layout.<p>
0979:             * 
0980:             * Most applications do not call this method directly.
0981:             *
0982:             * @param parent   the container in which to do the layout
0983:             * @return the preferred size of the <code>parent</code> container
0984:             * 
0985:             * @see Container#getPreferredSize()
0986:             */
0987:            public Dimension preferredLayoutSize(Container parent) {
0988:                return computeLayoutSize(parent, preferredWidthMeasure,
0989:                        preferredHeightMeasure);
0990:            }
0991:
0992:            /**
0993:             * Returns the maximum dimensions for this layout given the components
0994:             * in the specified target container.
0995:             * 
0996:             * @param target    the container which needs to be laid out
0997:             * @see Container
0998:             * @see #minimumLayoutSize(Container)
0999:             * @see #preferredLayoutSize(Container)
1000:             * @return the maximum dimensions for this layout
1001:             */
1002:            public Dimension maximumLayoutSize(Container target) {
1003:                return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1004:            }
1005:
1006:            /**
1007:             * Returns the alignment along the x axis.  This specifies how
1008:             * the component would like to be aligned relative to other
1009:             * components.  The value should be a number between 0 and 1
1010:             * where 0 represents alignment along the origin, 1 is aligned
1011:             * the furthest away from the origin, 0.5 is centered, etc.
1012:             * 
1013:             * @param parent   the parent container
1014:             * @return the value <code>0.5f</code> to indicate center alignment
1015:             */
1016:            public float getLayoutAlignmentX(Container parent) {
1017:                return 0.5f;
1018:            }
1019:
1020:            /**
1021:             * Returns the alignment along the y axis.  This specifies how
1022:             * the component would like to be aligned relative to other
1023:             * components.  The value should be a number between 0 and 1
1024:             * where 0 represents alignment along the origin, 1 is aligned
1025:             * the furthest away from the origin, 0.5 is centered, etc.
1026:             * 
1027:             * @param parent  the parent container
1028:             * @return the value <code>0.5f</code> to indicate center alignment
1029:             */
1030:            public float getLayoutAlignmentY(Container parent) {
1031:                return 0.5f;
1032:            }
1033:
1034:            /**
1035:             * Invalidates the layout, indicating that if the layout manager
1036:             * has cached information it should be discarded.
1037:             * 
1038:             * @param target   the container that holds the layout to be invalidated
1039:             */
1040:            public void invalidateLayout(Container target) {
1041:                invalidateCaches();
1042:            }
1043:
1044:            /**
1045:             * Lays out the specified container using this form layout.  This method
1046:             * reshapes components in the specified container in order to satisfy the
1047:             * contraints of this <code>FormLayout</code> object.<p>
1048:             * 
1049:             * Most applications do not call this method directly.<p>
1050:             * 
1051:             * The form layout performs the following steps:
1052:             * <ol>
1053:             * <li>find components that occupy exactly one column or row
1054:             * <li>compute minimum widths and heights 
1055:             * <li>compute preferred widths and heights
1056:             * <li>give cols and row equal size if they share a group
1057:             * <li>compress default columns and rows if total is less than pref size
1058:             * <li>give cols and row equal size if they share a group
1059:             * <li>distribute free space
1060:             * <li>set components bounds
1061:             * </ol>
1062:             * 
1063:             * @param parent	the container in which to do the layout
1064:             * @see Container
1065:             * @see Container#doLayout()
1066:             */
1067:            public void layoutContainer(Container parent) {
1068:                synchronized (parent.getTreeLock()) {
1069:                    initializeColAndRowComponentLists();
1070:                    Dimension size = parent.getSize();
1071:
1072:                    Insets insets = parent.getInsets();
1073:                    int totalWidth = size.width - insets.left - insets.right;
1074:                    int totalHeight = size.height - insets.top - insets.bottom;
1075:
1076:                    int[] x = computeGridOrigins(parent, totalWidth,
1077:                            insets.left, colSpecs, colComponents,
1078:                            colGroupIndices, minimumWidthMeasure,
1079:                            preferredWidthMeasure);
1080:                    int[] y = computeGridOrigins(parent, totalHeight,
1081:                            insets.top, rowSpecs, rowComponents,
1082:                            rowGroupIndices, minimumHeightMeasure,
1083:                            preferredHeightMeasure);
1084:
1085:                    layoutComponents(x, y);
1086:                }
1087:            }
1088:
1089:            // Layout Algorithm *****************************************************
1090:
1091:            /**
1092:             * Initializes two lists for columns and rows that hold a column's 
1093:             * or row's components that span only this column or row.<p>
1094:             * 
1095:             * Iterates over all components and their associated constraints; 
1096:             * every component that has a column span or row span of 1 
1097:             * is put into the column's or row's component list.<p>
1098:             * 
1099:             * As of the Forms version 1.0.x invisible components are not taken
1100:             * into account when the container is layed out. See the TODO in the
1101:             * JavaDoc class commment for details on this issue.
1102:             */
1103:            private void initializeColAndRowComponentLists() {
1104:                colComponents = new LinkedList[getColumnCount()];
1105:                for (int i = 0; i < getColumnCount(); i++) {
1106:                    colComponents[i] = new LinkedList();
1107:                }
1108:
1109:                rowComponents = new LinkedList[getRowCount()];
1110:                for (int i = 0; i < getRowCount(); i++) {
1111:                    rowComponents[i] = new LinkedList();
1112:                }
1113:
1114:                for (Iterator i = constraintMap.entrySet().iterator(); i
1115:                        .hasNext();) {
1116:                    Map.Entry entry = (Map.Entry) i.next();
1117:                    Component component = (Component) entry.getKey();
1118:                    if (!component.isVisible())
1119:                        continue;
1120:
1121:                    CellConstraints constraints = (CellConstraints) entry
1122:                            .getValue();
1123:                    if (constraints.gridWidth == 1)
1124:                        colComponents[constraints.gridX - 1].add(component);
1125:
1126:                    if (constraints.gridHeight == 1)
1127:                        rowComponents[constraints.gridY - 1].add(component);
1128:                }
1129:            }
1130:
1131:            /**
1132:             * Computes and returns the layout size of the given <code>parent</code>
1133:             * container using the specified measures.
1134:             *
1135:             * @param parent   the container in which to do the layout
1136:             * @param defaultWidthMeasure   the measure used to compute the default width
1137:             * @param defaultHeightMeasure  the measure used to compute the default height
1138:             * @return the layout size of the <code>parent</code> container
1139:             */
1140:            private Dimension computeLayoutSize(Container parent,
1141:                    Measure defaultWidthMeasure, Measure defaultHeightMeasure) {
1142:                synchronized (parent.getTreeLock()) {
1143:                    initializeColAndRowComponentLists();
1144:                    int[] colWidths = maximumSizes(parent, colSpecs,
1145:                            colComponents, minimumWidthMeasure,
1146:                            preferredWidthMeasure, defaultWidthMeasure);
1147:                    int[] rowHeights = maximumSizes(parent, rowSpecs,
1148:                            rowComponents, minimumHeightMeasure,
1149:                            preferredHeightMeasure, defaultHeightMeasure);
1150:                    int[] groupedWidths = groupedSizes(colGroupIndices,
1151:                            colWidths);
1152:                    int[] groupedHeights = groupedSizes(rowGroupIndices,
1153:                            rowHeights);
1154:
1155:                    // Convert sizes to origins.
1156:                    int[] xOrigins = computeOrigins(groupedWidths, 0);
1157:                    int[] yOrigins = computeOrigins(groupedHeights, 0);
1158:
1159:                    int width1 = sum(groupedWidths);
1160:                    int height1 = sum(groupedHeights);
1161:                    int maxWidth = width1;
1162:                    int maxHeight = height1;
1163:
1164:                    /*
1165:                     * Take components that span multiple columns or rows into account.
1166:                     * This shall be done if and only if a component spans an interval
1167:                     * that can grow. 
1168:                     */
1169:                    // First computes the maximum number of cols/rows a component
1170:                    // can span without spanning a growing column.
1171:                    int[] maxFixedSizeColsTable = computeMaximumFixedSpanTable(colSpecs);
1172:                    int[] maxFixedSizeRowsTable = computeMaximumFixedSpanTable(rowSpecs);
1173:
1174:                    for (Iterator i = constraintMap.entrySet().iterator(); i
1175:                            .hasNext();) {
1176:                        Map.Entry entry = (Map.Entry) i.next();
1177:                        Component component = (Component) entry.getKey();
1178:                        if (!component.isVisible())
1179:                            continue;
1180:
1181:                        CellConstraints constraints = (CellConstraints) entry
1182:                                .getValue();
1183:                        if ((constraints.gridWidth > 1)
1184:                                && (constraints.gridWidth > maxFixedSizeColsTable[constraints.gridX - 1])) {
1185:                            //int compWidth = minimumWidthMeasure.sizeOf(component);
1186:                            int compWidth = defaultWidthMeasure
1187:                                    .sizeOf(component);
1188:                            //int compWidth = preferredWidthMeasure.sizeOf(component);
1189:                            int gridX1 = constraints.gridX - 1;
1190:                            int gridX2 = gridX1 + constraints.gridWidth;
1191:                            int lead = xOrigins[gridX1];
1192:                            int trail = width1 - xOrigins[gridX2];
1193:                            int myWidth = lead + compWidth + trail;
1194:                            if (myWidth > maxWidth) {
1195:                                maxWidth = myWidth;
1196:                            }
1197:                        }
1198:
1199:                        if ((constraints.gridHeight > 1)
1200:                                && (constraints.gridHeight > maxFixedSizeRowsTable[constraints.gridY - 1])) {
1201:                            //int compHeight = minimumHeightMeasure.sizeOf(component);
1202:                            int compHeight = defaultHeightMeasure
1203:                                    .sizeOf(component);
1204:                            //int compHeight = preferredHeightMeasure.sizeOf(component);
1205:                            int gridY1 = constraints.gridY - 1;
1206:                            int gridY2 = gridY1 + constraints.gridHeight;
1207:                            int lead = yOrigins[gridY1];
1208:                            int trail = height1 - yOrigins[gridY2];
1209:                            int myHeight = lead + compHeight + trail;
1210:                            if (myHeight > maxHeight) {
1211:                                maxHeight = myHeight;
1212:                            }
1213:                        }
1214:                    }
1215:                    Insets insets = parent.getInsets();
1216:                    int width = maxWidth + insets.left + insets.right;
1217:                    int height = maxHeight + insets.top + insets.bottom;
1218:                    return new Dimension(width, height);
1219:                }
1220:            }
1221:
1222:            /**
1223:             * Computes and returns the grid's origins.
1224:             * 
1225:             * @param container         the layout container
1226:             * @param totalSize         the total size to assign
1227:             * @param offset     		the offset from left or top margin
1228:             * @param formSpecs     	the column or row specs, resp.
1229:             * @param componentLists	the components list for each col/row
1230:             * @param minMeasure		the measure used to determin min sizes
1231:             * @param prefMeasure		the measure used to determin pre sizes
1232:             * @param groupIndices		the group specification
1233:             * @return an int array with the origins
1234:             */
1235:            private int[] computeGridOrigins(Container container,
1236:                    int totalSize, int offset, List formSpecs,
1237:                    List[] componentLists, int[][] groupIndices,
1238:                    Measure minMeasure, Measure prefMeasure) {
1239:                /* For each spec compute the minimum and preferred size that is 
1240:                 * the maximum of all component minimum and preferred sizes resp.
1241:                 */
1242:                int[] minSizes = maximumSizes(container, formSpecs,
1243:                        componentLists, minMeasure, prefMeasure, minMeasure);
1244:                int[] prefSizes = maximumSizes(container, formSpecs,
1245:                        componentLists, minMeasure, prefMeasure, prefMeasure);
1246:
1247:                int[] groupedMinSizes = groupedSizes(groupIndices, minSizes);
1248:                int[] groupedPrefSizes = groupedSizes(groupIndices, prefSizes);
1249:                int totalMinSize = sum(groupedMinSizes);
1250:                int totalPrefSize = sum(groupedPrefSizes);
1251:                int[] compressedSizes = compressedSizes(formSpecs, totalSize,
1252:                        totalMinSize, totalPrefSize, groupedMinSizes, prefSizes);
1253:                int[] groupedSizes = groupedSizes(groupIndices, compressedSizes);
1254:                int totalGroupedSize = sum(groupedSizes);
1255:                int[] sizes = distributedSizes(formSpecs, totalSize,
1256:                        totalGroupedSize, groupedSizes);
1257:                return computeOrigins(sizes, offset);
1258:            }
1259:
1260:            /**
1261:             * Computes origins from sizes taking the specified offset into account.
1262:             * 
1263:             * @param sizes     the array of sizes
1264:             * @param offset    an offset for the first origin
1265:             * @return an array of origins
1266:             */
1267:            private int[] computeOrigins(int[] sizes, int offset) {
1268:                int count = sizes.length;
1269:                int[] origins = new int[count + 1];
1270:                origins[0] = offset;
1271:                for (int i = 1; i <= count; i++) {
1272:                    origins[i] = origins[i - 1] + sizes[i - 1];
1273:                }
1274:                return origins;
1275:            }
1276:
1277:            /**
1278:             * Lays out the components using the given x and y origins, the column 
1279:             * and row specifications, and the component constraints.<p>
1280:             * 
1281:             * The actual computation is done by each component's form constraint
1282:             * object. We just compute the cell, the cell bounds and then hand over
1283:             * the component, cell bounds, and measure to the form constraints.
1284:             * This will allow potential subclasses of <code>CellConstraints</code>
1285:             * to do special micro-layout corrections. For example, such a subclass
1286:             * could map JComponent classes to visual layout bounds that may
1287:             * lead to a slightly different bounds.
1288:             * 
1289:             * @param x     an int array of the horizontal origins 
1290:             * @param y     an int array of the vertical origins
1291:             */
1292:            private void layoutComponents(int[] x, int[] y) {
1293:                Rectangle cellBounds = new Rectangle();
1294:                for (Iterator i = constraintMap.entrySet().iterator(); i
1295:                        .hasNext();) {
1296:                    Map.Entry entry = (Map.Entry) i.next();
1297:                    Component component = (Component) entry.getKey();
1298:                    CellConstraints constraints = (CellConstraints) entry
1299:                            .getValue();
1300:
1301:                    int gridX = constraints.gridX - 1;
1302:                    int gridY = constraints.gridY - 1;
1303:                    int gridWidth = constraints.gridWidth;
1304:                    int gridHeight = constraints.gridHeight;
1305:                    cellBounds.x = x[gridX];
1306:                    cellBounds.y = y[gridY];
1307:                    cellBounds.width = x[gridX + gridWidth] - cellBounds.x;
1308:                    cellBounds.height = y[gridY + gridHeight] - cellBounds.y;
1309:
1310:                    constraints.setBounds(component, this , cellBounds,
1311:                            minimumWidthMeasure, minimumHeightMeasure,
1312:                            preferredWidthMeasure, preferredHeightMeasure);
1313:                }
1314:            }
1315:
1316:            /**
1317:             * Invalidates the component size caches.
1318:             */
1319:            private void invalidateCaches() {
1320:                componentSizeCache.invalidate();
1321:            }
1322:
1323:            /**
1324:             * Computes and returns the sizes for the given form specs, component
1325:             * lists and measures fot minimum, preferred, and default size.
1326:             * 
1327:             * @param container         the layout container
1328:             * @param formSpecs         the column or row specs, resp.
1329:             * @param componentLists    the components list for each col/row
1330:             * @param minMeasure        the measure used to determin min sizes
1331:             * @param prefMeasure       the measure used to determin pre sizes
1332:             * @param defaultMeasure    the measure used to determin default sizes
1333:             * @return the column or row sizes
1334:             */
1335:            private int[] maximumSizes(Container container, List formSpecs,
1336:                    List[] componentLists, Measure minMeasure,
1337:                    Measure prefMeasure, Measure defaultMeasure) {
1338:                FormSpec formSpec;
1339:                int size = formSpecs.size();
1340:                int[] result = new int[size];
1341:                for (int i = 0; i < size; i++) {
1342:                    formSpec = (FormSpec) formSpecs.get(i);
1343:                    result[i] = formSpec.maximumSize(container,
1344:                            componentLists[i], minMeasure, prefMeasure,
1345:                            defaultMeasure);
1346:                }
1347:                return result;
1348:            }
1349:
1350:            /**
1351:             * Computes and returns the compressed sizes. Compresses space for columns
1352:             * and rows iff the available space is less than the total preferred size
1353:             * but more than the total minimum size.<p>
1354:             * 
1355:             * Only columns and rows that are specified to be compressable will be
1356:             * affected. You can specify a column and row as compressable by
1357:             * giving it the component size <tt>default</tt>.
1358:             * 
1359:             * @param formSpecs      the column or row specs to use
1360:             * @param totalSize      the total available size
1361:             * @param totalMinSize   the sum of all minimum sizes
1362:             * @param totalPrefSize  the sum of all preferred sizes
1363:             * @param minSizes       an int array of column/row minimum sizes
1364:             * @param prefSizes      an int array of column/row preferred sizes
1365:             * @return an int array of compressed column/row sizes
1366:             */
1367:            private int[] compressedSizes(List formSpecs, int totalSize,
1368:                    int totalMinSize, int totalPrefSize, int[] minSizes,
1369:                    int[] prefSizes) {
1370:
1371:                // If we have less space than the total min size, answer the min sizes.                            
1372:                if (totalSize < totalMinSize)
1373:                    return minSizes;
1374:                // If we have more space than the total pref size, answer the pref sizes.                            
1375:                if (totalSize >= totalPrefSize)
1376:                    return prefSizes;
1377:
1378:                int count = formSpecs.size();
1379:                int[] sizes = new int[count];
1380:
1381:                double totalCompressionSpace = totalPrefSize - totalSize;
1382:                double maxCompressionSpace = totalPrefSize - totalMinSize;
1383:                double compressionFactor = totalCompressionSpace
1384:                        / maxCompressionSpace;
1385:
1386:                //      System.out.println("Total compression space=" + totalCompressionSpace);
1387:                //      System.out.println("Max compression space  =" + maxCompressionSpace);
1388:                //      System.out.println("Compression factor     =" + compressionFactor);
1389:
1390:                for (int i = 0; i < count; i++) {
1391:                    FormSpec formSpec = (FormSpec) formSpecs.get(i);
1392:                    sizes[i] = prefSizes[i];
1393:                    if (formSpec.getSize().compressible()) {
1394:                        sizes[i] -= (int) Math
1395:                                .round((prefSizes[i] - minSizes[i])
1396:                                        * compressionFactor);
1397:                    }
1398:                }
1399:                return sizes;
1400:            }
1401:
1402:            /**
1403:             * Computes and returns the grouped sizes. 
1404:             * Gives grouped columns and rows the same size.
1405:             * 
1406:             * @param groups	the group specification
1407:             * @param rawSizes	the raw sizes before the grouping 
1408:             * @return the grouped sizes
1409:             */
1410:            private int[] groupedSizes(int[][] groups, int[] rawSizes) {
1411:                // Return the compressed sizes if there are no groups.
1412:                if (groups == null || groups.length == 0) {
1413:                    return rawSizes;
1414:                }
1415:
1416:                // Initialize the result with the given compressed sizes.
1417:                int[] sizes = new int[rawSizes.length];
1418:                for (int i = 0; i < sizes.length; i++) {
1419:                    sizes[i] = rawSizes[i];
1420:                }
1421:
1422:                // For each group equalize the sizes.
1423:                for (int group = 0; group < groups.length; group++) {
1424:                    int[] groupIndices = groups[group];
1425:                    int groupMaxSize = 0;
1426:                    // Compute the group's maximum size.
1427:                    for (int i = 0; i < groupIndices.length; i++) {
1428:                        int index = groupIndices[i] - 1;
1429:                        groupMaxSize = Math.max(groupMaxSize, sizes[index]);
1430:                    }
1431:                    // Set all sizes of this group to the group's maximum size.
1432:                    for (int i = 0; i < groupIndices.length; i++) {
1433:                        int index = groupIndices[i] - 1;
1434:                        sizes[index] = groupMaxSize;
1435:                    }
1436:                }
1437:                return sizes;
1438:            }
1439:
1440:            /**
1441:             * Distributes free space over columns and rows and 
1442:             * returns the sizes after this distribution process.
1443:             * 
1444:             * @param formSpecs      the column/row specifications to work with
1445:             * @param totalSize      the total available size
1446:             * @param totalPrefSize  the sum of all preferred sizes 
1447:             * @param inputSizes     the input sizes
1448:             * @return the distributed sizes
1449:             */
1450:            private int[] distributedSizes(List formSpecs, int totalSize,
1451:                    int totalPrefSize, int[] inputSizes) {
1452:                double totalFreeSpace = totalSize - totalPrefSize;
1453:                // Do nothing if there's no free space.
1454:                if (totalFreeSpace < 0)
1455:                    return inputSizes;
1456:
1457:                // Compute the total weight.
1458:                int count = formSpecs.size();
1459:                double totalWeight = 0.0;
1460:                for (int i = 0; i < count; i++) {
1461:                    FormSpec formSpec = (FormSpec) formSpecs.get(i);
1462:                    totalWeight += formSpec.getResizeWeight();
1463:                }
1464:
1465:                // Do nothing if there's no resizing column.
1466:                if (totalWeight == 0.0)
1467:                    return inputSizes;
1468:
1469:                int[] sizes = new int[count];
1470:
1471:                double restSpace = totalFreeSpace;
1472:                int roundedRestSpace = (int) totalFreeSpace;
1473:                for (int i = 0; i < count; i++) {
1474:                    FormSpec formSpec = (FormSpec) formSpecs.get(i);
1475:                    double weight = formSpec.getResizeWeight();
1476:                    if (weight == FormSpec.NO_GROW) {
1477:                        sizes[i] = inputSizes[i];
1478:                    } else {
1479:                        double roundingCorrection = restSpace
1480:                                - roundedRestSpace;
1481:                        double extraSpace = totalFreeSpace * weight
1482:                                / totalWeight;
1483:                        double correctedExtraSpace = extraSpace
1484:                                - roundingCorrection;
1485:                        int roundedExtraSpace = (int) Math
1486:                                .round(correctedExtraSpace);
1487:                        sizes[i] = inputSizes[i] + roundedExtraSpace;
1488:                        restSpace -= extraSpace;
1489:                        roundedRestSpace -= roundedExtraSpace;
1490:                    }
1491:                }
1492:                return sizes;
1493:            }
1494:
1495:            /**
1496:             * Computes and returns the sum of integers in the given array of ints.
1497:             * 
1498:             * @param sizes	   an array of ints to sum up
1499:             * @return the sum of ints in the array
1500:             */
1501:            private int sum(int[] sizes) {
1502:                int sum = 0;
1503:                for (int i = sizes.length - 1; i >= 0; i--) {
1504:                    sum += sizes[i];
1505:                }
1506:                return sum;
1507:            }
1508:
1509:            /**
1510:             * Computes and returns a table that maps a column/row index
1511:             * to the maximum number of columns/rows that a component can span
1512:             * without spanning a growing column.<p>
1513:             * 
1514:             * Iterates over the specs from right to left/bottom to top,
1515:             * sets the table value to zero if a spec can grow,
1516:             * otherwise increases the span by one.<p>
1517:             * 
1518:             * <strong>Examples:</strong><pre>
1519:             * "pref, 4dlu, pref, 2dlu, p:grow, 2dlu,      pref" ->
1520:             * [4,    3,    2,    1,    0,      MAX_VALUE, MAX_VALUE]
1521:             * 
1522:             * "p:grow, 4dlu, p:grow, 9dlu,      pref" ->
1523:             * [0,      1,    0,      MAX_VALUE, MAX_VALUE]
1524:             * 
1525:             * "p, 4dlu, p, 2dlu, 0:grow" ->
1526:             * [4, 3,    2, 1,    0]
1527:             * </pre>
1528:             * 
1529:             * @param formSpecs  the column specs or row specs
1530:             * @return a table that maps a spec index to the maximum span for 
1531:             *    fixed size specs
1532:             */
1533:            private int[] computeMaximumFixedSpanTable(List formSpecs) {
1534:                int size = formSpecs.size();
1535:                int[] table = new int[size];
1536:                int maximumFixedSpan = Integer.MAX_VALUE; // Could be 1
1537:                for (int i = size - 1; i >= 0; i--) {
1538:                    FormSpec spec = (FormSpec) formSpecs.get(i); // ArrayList access
1539:                    if (spec.canGrow()) {
1540:                        maximumFixedSpan = 0;
1541:                    }
1542:                    table[i] = maximumFixedSpan;
1543:                    if (maximumFixedSpan < Integer.MAX_VALUE)
1544:                        maximumFixedSpan++;
1545:                }
1546:                return table;
1547:            }
1548:
1549:            // Measuring Component Sizes ********************************************
1550:
1551:            /**
1552:             * An interface that describes how to measure a <code>Component</code>.
1553:             * Used to abstract from horizontal and vertical dimensions as well as
1554:             * minimum and preferred sizes.
1555:             * 
1556:             * @since 1.1
1557:             */
1558:            public static interface Measure {
1559:
1560:                /**
1561:                 * Computes and returns the size of the given <code>Component</code>.
1562:                 * 
1563:                 * @param component  the component to measure
1564:                 * @return the component's size
1565:                 */
1566:                int sizeOf(Component component);
1567:            }
1568:
1569:            /**
1570:             * An abstract implementation of the <code>Measure</code> interface
1571:             * that caches component sizes.
1572:             */
1573:            private abstract static class CachingMeasure implements  Measure,
1574:                    Serializable {
1575:
1576:                /**
1577:                 * Holds previously requested component sizes. 
1578:                 * Used to minimize size requests to subcomponents. 
1579:                 */
1580:                protected final ComponentSizeCache cache;
1581:
1582:                private CachingMeasure(ComponentSizeCache cache) {
1583:                    this .cache = cache;
1584:                }
1585:
1586:            }
1587:
1588:            /**
1589:             * Measures a component by computing its minimum width.
1590:             */
1591:            private static final class MinimumWidthMeasure extends
1592:                    CachingMeasure {
1593:                private MinimumWidthMeasure(ComponentSizeCache cache) {
1594:                    super (cache);
1595:                }
1596:
1597:                public int sizeOf(Component c) {
1598:                    return cache.getMinimumSize(c).width;
1599:                }
1600:            }
1601:
1602:            /**
1603:             * Measures a component by computing its minimum height.
1604:             */
1605:            private static final class MinimumHeightMeasure extends
1606:                    CachingMeasure {
1607:                private MinimumHeightMeasure(ComponentSizeCache cache) {
1608:                    super (cache);
1609:                }
1610:
1611:                public int sizeOf(Component c) {
1612:                    return cache.getMinimumSize(c).height;
1613:                }
1614:            }
1615:
1616:            /**
1617:             * Measures a component by computing its preferred width.
1618:             */
1619:            private static final class PreferredWidthMeasure extends
1620:                    CachingMeasure {
1621:                private PreferredWidthMeasure(ComponentSizeCache cache) {
1622:                    super (cache);
1623:                }
1624:
1625:                public int sizeOf(Component c) {
1626:                    return cache.getPreferredSize(c).width;
1627:                }
1628:            }
1629:
1630:            /**
1631:             * Measures a component by computing its preferred height.
1632:             */
1633:            private static final class PreferredHeightMeasure extends
1634:                    CachingMeasure {
1635:                private PreferredHeightMeasure(ComponentSizeCache cache) {
1636:                    super (cache);
1637:                }
1638:
1639:                public int sizeOf(Component c) {
1640:                    return cache.getPreferredSize(c).height;
1641:                }
1642:            }
1643:
1644:            // Caching Component Sizes **********************************************
1645:
1646:            /**
1647:             * A cache for component minimum and preferred sizes.
1648:             * Used to reduce the requests to determine a component's size.
1649:             */
1650:            private static final class ComponentSizeCache implements 
1651:                    Serializable {
1652:
1653:                /** Maps components to their minimum sizes.  */
1654:                private final Map minimumSizes;
1655:
1656:                /** Maps components to their preferred sizes. */
1657:                private final Map preferredSizes;
1658:
1659:                /**
1660:                 * Constructs a <code>ComponentSizeCache</code>.
1661:                 * 
1662:                 * @param initialCapacity	the initial cache capacity
1663:                 */
1664:                private ComponentSizeCache(int initialCapacity) {
1665:                    minimumSizes = new HashMap(initialCapacity);
1666:                    preferredSizes = new HashMap(initialCapacity);
1667:                }
1668:
1669:                /**
1670:                 * Invalidates the cache. Clears all stored size information.
1671:                 */
1672:                void invalidate() {
1673:                    minimumSizes.clear();
1674:                    preferredSizes.clear();
1675:                }
1676:
1677:                /**
1678:                 * Returns the minimum size for the given component. Tries to look up
1679:                 * the value from the cache; lazily creates the value if it has not
1680:                 * been requested before.
1681:                 * 
1682:                 * @param component	the component to compute the minimum size
1683:                 * @return the component's minimum size
1684:                 */
1685:                Dimension getMinimumSize(Component component) {
1686:                    Dimension size = (Dimension) minimumSizes.get(component);
1687:                    if (size == null) {
1688:                        size = component.getMinimumSize();
1689:                        minimumSizes.put(component, size);
1690:                    }
1691:                    return size;
1692:                }
1693:
1694:                /**
1695:                 * Returns the preferred size for the given component. Tries to look
1696:                 * up the value from the cache; lazily creates the value if it has not
1697:                 * been requested before.
1698:                 * 
1699:                 * @param component 	the component to compute the preferred size
1700:                 * @return the component's preferred size
1701:                 */
1702:                Dimension getPreferredSize(Component component) {
1703:                    Dimension size = (Dimension) preferredSizes.get(component);
1704:                    if (size == null) {
1705:                        size = component.getPreferredSize();
1706:                        preferredSizes.put(component, size);
1707:                    }
1708:                    return size;
1709:                }
1710:
1711:                void removeEntry(Component component) {
1712:                    minimumSizes.remove(component);
1713:                    preferredSizes.remove(component);
1714:                }
1715:            }
1716:
1717:            // Exposing the Layout Information **************************************
1718:
1719:            /**
1720:             * Computes and returns the horizontal and vertical grid origins.
1721:             * Performs the same layout process as <code>#layoutContainer</code> 
1722:             * but does not layout the components.<p>
1723:             * 
1724:             * This method has been added only to make it easier to debug
1725:             * the form layout. <strong>You must not call this method directly;
1726:             * It may be removed in a future release or the visibility
1727:             * may be reduced.</strong> 
1728:             * 
1729:             * @param parent   the <code>Container</code> to inspect
1730:             * @return an object that comprises the grid x and y origins
1731:             */
1732:            public LayoutInfo getLayoutInfo(Container parent) {
1733:                synchronized (parent.getTreeLock()) {
1734:                    initializeColAndRowComponentLists();
1735:                    Dimension size = parent.getSize();
1736:
1737:                    Insets insets = parent.getInsets();
1738:                    int totalWidth = size.width - insets.left - insets.right;
1739:                    int totalHeight = size.height - insets.top - insets.bottom;
1740:
1741:                    int[] x = computeGridOrigins(parent, totalWidth,
1742:                            insets.left, colSpecs, colComponents,
1743:                            colGroupIndices, minimumWidthMeasure,
1744:                            preferredWidthMeasure);
1745:                    int[] y = computeGridOrigins(parent, totalHeight,
1746:                            insets.top, rowSpecs, rowComponents,
1747:                            rowGroupIndices, minimumHeightMeasure,
1748:                            preferredHeightMeasure);
1749:                    return new LayoutInfo(x, y);
1750:                }
1751:            }
1752:
1753:            /**
1754:             * Stores column and row origins. 
1755:             */
1756:            public static final class LayoutInfo {
1757:
1758:                /**
1759:                 * Holds the origins of the columns.
1760:                 */
1761:                public final int[] columnOrigins;
1762:
1763:                /**
1764:                 * Holds the origins of the rows. 
1765:                 */
1766:                public final int[] rowOrigins;
1767:
1768:                private LayoutInfo(int[] xOrigins, int[] yOrigins) {
1769:                    this .columnOrigins = xOrigins;
1770:                    this .rowOrigins = yOrigins;
1771:                }
1772:
1773:                /**
1774:                 * Returns the layout's horizontal origin, the origin of the first column.
1775:                 * 
1776:                 * @return the layout's horizontal origin, the origin of the first column.
1777:                 */
1778:                public int getX() {
1779:                    return columnOrigins[0];
1780:                }
1781:
1782:                /**
1783:                 * Returns the layout's vertical origin, the origin of the first row.
1784:                 * 
1785:                 * @return the layout's vertical origin, the origin of the first row.
1786:                 */
1787:                public int getY() {
1788:                    return rowOrigins[0];
1789:                }
1790:
1791:                /**
1792:                 * Returns the layout's width, the size between the first and the last
1793:                 * column origin.
1794:                 * 
1795:                 * @return the layout's width.
1796:                 */
1797:                public int getWidth() {
1798:                    return columnOrigins[columnOrigins.length - 1]
1799:                            - columnOrigins[0];
1800:                }
1801:
1802:                /**
1803:                 * Returns the layout's height, the size between the first and last row.
1804:                 * 
1805:                 * @return the layout's height.
1806:                 */
1807:                public int getHeight() {
1808:                    return rowOrigins[rowOrigins.length - 1] - rowOrigins[0];
1809:                }
1810:
1811:            }
1812:
1813:            // Helper Code **********************************************************
1814:
1815:            /**
1816:             * Creates and returns a deep copy of the given array. 
1817:             * Unlike <code>#clone</code> that performs a shallow copy, 
1818:             * this method copies both array levels.
1819:             * 
1820:             * @param array   the array to clone
1821:             * @return a deep copy of the given array
1822:             * 
1823:             * @see Object#clone()
1824:             */
1825:            private int[][] deepClone(int[][] array) {
1826:                int[][] result = new int[array.length][];
1827:                for (int i = 0; i < result.length; i++) {
1828:                    result[i] = (int[]) array[i].clone();
1829:                }
1830:                return result;
1831:            }
1832:
1833:            // Serialization ********************************************************
1834:
1835:            /**
1836:             * In addition to the default serialization mechanism this class
1837:             * invalidates the component size cache. The cache will be populated
1838:             * again after the deserialization.
1839:             * Also, the fields <code>colComponents</code> and 
1840:             * <code>rowComponents</code> have been marked as transient 
1841:             * to exclude them from the serialization.
1842:             */
1843:            private void writeObject(ObjectOutputStream out) throws IOException {
1844:                invalidateCaches();
1845:                out.defaultWriteObject();
1846:            }
1847:
1848:            // Debug Helper Code ****************************************************
1849:
1850:            /*
1851:            // Prints the given column widths and row heights. 
1852:            private void printSizes(String title, int[] colWidths, int[] rowHeights) {
1853:                System.out.println();
1854:                System.out.println(title);
1855:                int totalWidth = 0;
1856:                System.out.print("Column widths: ");
1857:                for (int i=0; i < getColumnCount(); i++) {
1858:                    int width = colWidths[i];
1859:                    totalWidth += width;
1860:                    System.out.print(width + ", ");
1861:                }
1862:                System.out.println(" Total=" + totalWidth);
1863:                
1864:                int totalHeight = 0;
1865:                System.out.print("Row heights:   ");
1866:                for (int i=0; i < getRowCount(); i++) {
1867:                    int height = rowHeights[i];
1868:                    totalHeight += height;
1869:                    System.out.print(height + ", ");
1870:                }
1871:                System.out.println(" Total=" + totalHeight);
1872:                System.out.println();
1873:            }
1874:
1875:             */
1876:
1877:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.