Source Code Cross Referenced for FormLayout.java in  » Swing-Library » abeille-forms-designer » 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 » abeille forms designer » com.jgoodies.forms.layout 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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