Source Code Cross Referenced for LayoutDesigner.java in  » IDE-Netbeans » form » org » netbeans » modules » form » layoutdesign » 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 » IDE Netbeans » form » org.netbeans.modules.form.layoutdesign 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:
0042:        package org.netbeans.modules.form.layoutdesign;
0043:
0044:        import java.awt.BasicStroke;
0045:        import java.awt.Dimension;
0046:        import java.awt.Graphics2D;
0047:        import java.awt.Image;
0048:        import java.awt.Point;
0049:        import java.awt.Rectangle;
0050:        import java.awt.RenderingHints;
0051:        import java.awt.Stroke;
0052:        import java.util.*;
0053:        import org.openide.loaders.DataObject;
0054:        import org.openide.util.Utilities;
0055:
0056:        public class LayoutDesigner implements  LayoutConstants {
0057:
0058:            private LayoutModel layoutModel;
0059:
0060:            private VisualMapper visualMapper;
0061:
0062:            private LayoutDragger dragger;
0063:
0064:            private LayoutOperations operations;
0065:
0066:            private Listener modelListener;
0067:
0068:            private boolean imposeSize = true;
0069:            private boolean optimizeStructure = true;
0070:            private boolean visualStateUpToDate;
0071:
0072:            // -----
0073:
0074:            public LayoutDesigner(LayoutModel model, VisualMapper mapper) {
0075:                layoutModel = model;
0076:                visualMapper = mapper;
0077:                operations = new LayoutOperations(model, mapper);
0078:                modelListener = new Listener();
0079:                modelListener.activate();
0080:            }
0081:
0082:            // -------
0083:            // updates of the current visual state stored in the model
0084:
0085:            public boolean updateCurrentState() {
0086:                if (logTestCode()) {
0087:                    testCode.add("// > UPDATE CURRENT STATE"); //NOI18N
0088:                }
0089:                Object changeMark = layoutModel.getChangeMark();
0090:                boolean changeRequired = imposeSize || optimizeStructure;
0091:                Set<LayoutComponent> updatedContainers = changeRequired ? new HashSet<LayoutComponent>()
0092:                        : null;
0093:                try {
0094:                    if (changeRequired) {
0095:                        modelListener.deactivate(); // some changes may happen...
0096:                        destroyRedundantGroups(updatedContainers);
0097:                        operations.mergeAdjacentGaps(updatedContainers);
0098:                    }
0099:
0100:                    updatePositions(updatedContainers);
0101:                } finally {
0102:                    if (changeRequired) {
0103:                        modelListener.activate();
0104:                    }
0105:                }
0106:
0107:                if (changeRequired) {
0108:                    imposeSize = optimizeStructure = false;
0109:
0110:                    if (updatedContainers != null) {
0111:                        Iterator<LayoutComponent> it = updatedContainers
0112:                                .iterator();
0113:                        while (it.hasNext()) {
0114:                            LayoutComponent cont = it.next();
0115:                            visualMapper.rebuildLayout(cont.getId());
0116:                        }
0117:                        updatePositions(null);
0118:                    }
0119:                }
0120:
0121:                if (logTestCode()) {
0122:                    testCode.add("ld.updateCurrentState();"); //NOI18N
0123:                    testCode.add("// < UPDATE CURRENT STATE"); //NOI18N
0124:                }
0125:
0126:                visualStateUpToDate = true;
0127:                return !changeMark.equals(layoutModel.getChangeMark());
0128:            }
0129:
0130:            public void externalSizeChangeHappened() {
0131:                imposeSize = true;
0132:                visualStateUpToDate = false;
0133:                if (logTestCode()) {
0134:                    testCode.add("ld.externalSizeChangeHappened();"); // NOI18N
0135:                }
0136:            }
0137:
0138:            void requireStructureOptimization() {
0139:                optimizeStructure = true;
0140:                visualStateUpToDate = false;
0141:
0142:                Iterator it = layoutModel.getAllComponents();
0143:                while (it.hasNext()) {
0144:                    LayoutComponent comp = (LayoutComponent) it.next();
0145:                    if (comp.isLayoutContainer()) {
0146:                        LayoutInterval[] roots = getActiveLayoutRoots(comp);
0147:                        for (int dim = 0; dim < DIM_COUNT; dim++) {
0148:                            cleanDesignAttrs(roots[dim]);
0149:                        }
0150:                    }
0151:                }
0152:            }
0153:
0154:            private void updatePositions(Set<LayoutComponent> updatedContainers) {
0155:                Iterator it = layoutModel.getAllComponents();
0156:                while (it.hasNext()) {
0157:                    LayoutComponent comp = (LayoutComponent) it.next();
0158:                    if (!comp.isLayoutContainer())
0159:                        continue;
0160:
0161:                    if (optimizeStructure || imposeSize) {
0162:                        // make sure the layout definition reflects the current size
0163:                        if (imposeCurrentContainerSize(comp, null, false)) {
0164:                            updatedContainers.add(comp);
0165:                            if (optimizeStructure) {
0166:                                for (LayoutInterval[] roots : comp
0167:                                        .getLayoutRoots()) {
0168:                                    for (int dim = 0; dim < DIM_COUNT; dim++) {
0169:                                        optimizeGaps(roots[dim], dim, true);
0170:                                        destroyRedundantGroups(roots[dim]);
0171:                                    }
0172:                                }
0173:                            }
0174:                            LayoutInterval[] roots = getActiveLayoutRoots(comp);
0175:                            for (int dim = 0; dim < DIM_COUNT; dim++) {
0176:                                updateDesignModifications(roots[dim], dim);
0177:                            }
0178:                        }
0179:                    } else { // just update the current visual positions (LayoutRegion)
0180:                        Rectangle bounds = visualMapper
0181:                                .getContainerInterior(comp.getId());
0182:                        if (bounds != null) {
0183:                            comp.setCurrentInterior(bounds);
0184:                            for (LayoutComponent subComp : comp
0185:                                    .getSubcomponents()) {
0186:                                bounds = visualMapper
0187:                                        .getComponentBounds(subComp.getId());
0188:                                int baseline = visualMapper
0189:                                        .getBaselinePosition(subComp.getId(),
0190:                                                bounds.width, bounds.height);
0191:                                subComp.setCurrentBounds(bounds, baseline);
0192:                            }
0193:                            for (LayoutInterval[] roots : comp.getLayoutRoots()) {
0194:                                for (int i = 0; i < DIM_COUNT; i++) {
0195:                                    updateLayoutStructure(roots[i], i, false);
0196:                                }
0197:                            }
0198:                        }
0199:                    }
0200:                }
0201:            }
0202:
0203:            // recursive
0204:            /**
0205:             * Updates current space (LayoutRegion) of all groups in the layout.
0206:             * Also for all intervals having the preferred size explicitly defined the
0207:             * pref size is updated according to the current state. Min and max sizes
0208:             * as well if they are the same as pref.
0209:             */
0210:            void updateLayoutStructure(LayoutInterval interval, int dimension,
0211:                    boolean imposeGaps) {
0212:                LayoutRegion space = interval.getCurrentSpace();
0213:                boolean baseline = interval.getGroupAlignment() == BASELINE;
0214:
0215:                boolean first = true;
0216:                boolean firstResizingSpace = false;
0217:                int leadingSpace = 0;
0218:                boolean skipNext = false;
0219:
0220:                Iterator it = interval.getSubIntervals();
0221:                while (it.hasNext()) {
0222:                    LayoutInterval sub = (LayoutInterval) it.next();
0223:                    if (sub.isEmptySpace()) {
0224:                        if (!interval.isSequential()) {
0225:                            // filling gap in empty root group
0226:                            assert interval.getParent() == null;
0227:                            if (imposeGaps && space.isSet(dimension)) {
0228:                                imposeCurrentGapSize(sub,
0229:                                        space.size(dimension), dimension);
0230:                            }
0231:                        } else if (first || !it.hasNext()) {
0232:                            // first or last gap in sequence
0233:                            int min = sub.getMinimumSize(true);
0234:                            int pref = sub.getPreferredSize(true);
0235:                            int max = sub.getMaximumSize(true);
0236:                            if ((min == pref || min == USE_PREFERRED_SIZE)
0237:                                    && (pref == max || max == USE_PREFERRED_SIZE)
0238:                                    && pref != NOT_EXPLICITLY_DEFINED) {
0239:                                // Fixed size gap
0240:                                if (first) {
0241:                                    leadingSpace = pref;
0242:                                } else {
0243:                                    space.reshape(dimension, TRAILING, pref);
0244:                                }
0245:                            } else {
0246:                                int currentPref = LayoutRegion.UNKNOWN;
0247:                                LayoutInterval sibling = LayoutInterval
0248:                                        .getNeighbor(sub, SEQUENTIAL,
0249:                                                first ? LEADING : TRAILING);
0250:                                if (sibling == null) {
0251:                                    LayoutRegion rootSpace = LayoutInterval
0252:                                            .getRoot(interval)
0253:                                            .getCurrentSpace();
0254:                                    if (first) {
0255:                                        firstResizingSpace = true;
0256:                                        leadingSpace = -rootSpace.positions[dimension][LEADING];
0257:                                    } else { // last
0258:                                        currentPref = rootSpace.positions[dimension][TRAILING]
0259:                                                - space.positions[dimension][TRAILING];
0260:                                        space.reshape(dimension, TRAILING,
0261:                                                currentPref);
0262:                                    }
0263:                                } else if (sibling.isEmptySpace()) {
0264:                                    LayoutInterval parent = interval
0265:                                            .getParent();
0266:                                    LayoutInterval alignedParent = parent;
0267:                                    int align = first ? LEADING : TRAILING;
0268:                                    while (parent != null
0269:                                            && LayoutInterval
0270:                                                    .isAlignedAtBorder(
0271:                                                            interval, parent,
0272:                                                            align)) {
0273:                                        alignedParent = parent;
0274:                                        parent = parent.getParent();
0275:                                    }
0276:                                    LayoutInterval parComp = LayoutUtils
0277:                                            .getOutermostComponent(
0278:                                                    alignedParent, dimension,
0279:                                                    align);
0280:                                    // [this assert is maybe too strong - it's ok if the component at least touches the border - but that's hard to determine here]
0281:                                    //                            assert LayoutInterval.isAlignedAtBorder(parComp, interval.getParent(), first ? LEADING : TRAILING);
0282:                                    LayoutRegion parSpace = parComp
0283:                                            .getCurrentSpace();
0284:                                    if (first) {
0285:                                        firstResizingSpace = true;
0286:                                        leadingSpace = -parSpace.positions[dimension][LEADING];
0287:                                    } else {
0288:                                        currentPref = parSpace.positions[dimension][TRAILING]
0289:                                                - space.positions[dimension][TRAILING];
0290:                                        space.reshape(dimension, TRAILING,
0291:                                                currentPref);
0292:                                    }
0293:                                } else {
0294:                                    if (first) {
0295:                                        firstResizingSpace = true;
0296:                                        LayoutRegion sibSpace = sibling
0297:                                                .getCurrentSpace();
0298:                                        leadingSpace = -sibSpace.positions[dimension][TRAILING];
0299:                                    } else {
0300:                                        LayoutRegion sibSpace = sibling
0301:                                                .getCurrentSpace();
0302:                                        if (!sibling.isComponent()) {
0303:                                            sibSpace.reset();
0304:                                            updateLayoutStructure(sibling,
0305:                                                    dimension, imposeGaps);
0306:                                            skipNext = true;
0307:                                        }
0308:                                        int sibPos = sibSpace.positions[dimension][LEADING];
0309:                                        if (sibPos != LayoutRegion.UNKNOWN) { // Issue 62320
0310:                                            currentPref = sibPos
0311:                                                    - space.positions[dimension][TRAILING];
0312:                                            space.reshape(dimension, TRAILING,
0313:                                                    currentPref);
0314:                                        }
0315:                                    }
0316:                                }
0317:
0318:                                if (imposeGaps
0319:                                        && (currentPref != LayoutRegion.UNKNOWN)) { // last resizing gap
0320:                                    imposeCurrentGapSize(sub, currentPref,
0321:                                            dimension);
0322:                                }
0323:                            }
0324:                        } else if (imposeGaps) {
0325:                            LayoutInterval sibling = LayoutInterval
0326:                                    .getDirectNeighbor(sub, TRAILING, false);
0327:                            assert !sibling.isEmptySpace();
0328:                            LayoutRegion sibSpace = sibling.getCurrentSpace();
0329:                            if (!sibling.isComponent()) {
0330:                                sibSpace.reset();
0331:                                updateLayoutStructure(sibling, dimension,
0332:                                        imposeGaps);
0333:                                skipNext = true;
0334:                            }
0335:                            int currentSize = LayoutRegion.distance(space,
0336:                                    sibSpace, dimension, TRAILING, LEADING);
0337:                            imposeCurrentGapSize(sub, currentSize, dimension);
0338:                        }
0339:                        first = false;
0340:                        continue;
0341:                    }
0342:
0343:                    LayoutRegion subSpace = sub.getCurrentSpace();
0344:                    if (skipNext) { // this subgroup has been processed in advance
0345:                        skipNext = false;
0346:                    } else if (sub.isGroup()) {
0347:                        assert sub.getSubIntervalCount() > 0; // consistency check - there should be no empty groups
0348:                        subSpace.reset();
0349:                        /*if (interval.getParent() == null) {
0350:                            subSpace.set(space, dimension); // root space is known
0351:                        }*/
0352:                        updateLayoutStructure(sub, dimension, imposeGaps);
0353:                    }
0354:                    space.expand(subSpace);
0355:
0356:                    if (baseline && sub.isComponent()) {
0357:                        int baselinePos = subSpace.positions[dimension][BASELINE];
0358:                        if (baselinePos != LayoutRegion.UNKNOWN) {
0359:                            space.positions[dimension][BASELINE] = baselinePos;
0360:                            baseline = false;
0361:                        }
0362:                    }
0363:                    if (firstResizingSpace) {
0364:                        leadingSpace += space.positions[dimension][LEADING];
0365:                        firstResizingSpace = false;
0366:                        if (imposeGaps) {
0367:                            imposeCurrentGapSize(interval.getSubInterval(0),
0368:                                    leadingSpace, dimension);
0369:                        }
0370:                    }
0371:                    first = false;
0372:                }
0373:                if (leadingSpace != 0) {
0374:                    space.reshape(dimension, LEADING, -leadingSpace);
0375:                }
0376:            }
0377:
0378:            public void dumpTestcode(DataObject form) {
0379:                LayoutTestUtils.dumpTestcode(testCode, form, getModelCounter());
0380:                testCode = new ArrayList<String>();
0381:                testCode0 = new ArrayList<String>();
0382:                beforeMove = new ArrayList<String>();
0383:                move1 = new ArrayList<String>();
0384:                move2 = new ArrayList<String>();
0385:                isMoving = false;
0386:            }
0387:
0388:            // -----
0389:            // adding, moving, resizing
0390:
0391:            /**
0392:             * Determines a subset of components that can be visually dragged together.
0393:             * If the components are in diferrent containers then no dragging is
0394:             * possible (returns empty list). Otherwise the components that have the
0395:             * same parent and are under the same layout roots are returned.
0396:             * Nonexisting or parent-less components are ignored.
0397:             * @param component Ids of components selected for dragging
0398:             * @return List of Ids of components that can be dragged together
0399:             */
0400:            public List<String> getDraggableComponents(List<String> componentIds) {
0401:                LayoutComponent container = null;
0402:                List<String> draggable = null;
0403:                int commonLayer = -1;
0404:                for (String compId : componentIds) {
0405:                    LayoutComponent comp = layoutModel
0406:                            .getLayoutComponent(compId);
0407:                    if (comp == null || comp.getParent() == null) {
0408:                        continue;
0409:                    }
0410:                    if (container == null) {
0411:                        container = comp.getParent();
0412:                    } else if (comp.getParent() != container) {
0413:                        return Collections.emptyList();
0414:                    }
0415:
0416:                    int layerIndex = container.getLayoutRootsIndex(comp
0417:                            .getLayoutInterval(HORIZONTAL));
0418:                    assert layerIndex >= 0;
0419:                    if (layerIndex >= commonLayer) {
0420:                        if (commonLayer < 0) {
0421:                            draggable = new ArrayList<String>(componentIds
0422:                                    .size());
0423:                        } else if (layerIndex > commonLayer) {
0424:                            draggable.clear();
0425:                        }
0426:                        commonLayer = layerIndex;
0427:                        draggable.add(compId);
0428:                    }
0429:                }
0430:                if (draggable == null)
0431:                    draggable = Collections.emptyList();
0432:                return draggable;
0433:            }
0434:
0435:            /**
0436:             * Checks whether given component is "unplaced" - i.e. in other than default
0437:             * layer. Result of adding without mouse positioning (e.g. copying). In
0438:             * current model such a component needs to be moved additionally by the user
0439:             * to be placed in the default layer among other components.
0440:             * @param compId id of the component
0441:             * @return true if the component is placed in an additional layer
0442:             */
0443:            public boolean isUnplacedComponent(String compId) {
0444:                LayoutComponent comp = layoutModel.getLayoutComponent(compId);
0445:                if (comp != null) {
0446:                    LayoutComponent cont = comp.getParent();
0447:                    return cont != null
0448:                            && comp.getParentRoots()[HORIZONTAL] != cont
0449:                                    .getDefaultLayoutRoot(HORIZONTAL);
0450:                }
0451:                return false;
0452:            }
0453:
0454:            public void startAdding(LayoutComponent[] comps,
0455:                    Rectangle[] bounds, Point hotspot, String defaultContId) {
0456:                if (logTestCode()) {
0457:                    testCode.add("// > START ADDING"); //NOI18N
0458:                }
0459:                prepareDragger(comps, bounds, hotspot, LayoutDragger.ALL_EDGES);
0460:                if (logTestCode()) {
0461:                    testCode.add("{"); // NOI18N
0462:                    // lc should be already filled in the MetaComponentCreator.getPrecreatedComponent
0463:                    LayoutTestUtils.writeLayoutComponentArray(testCode,
0464:                            "comps", "lc"); //NOI18N
0465:                    LayoutTestUtils.writeRectangleArray(testCode, "bounds",
0466:                            bounds); //NOI18N
0467:                    LayoutTestUtils.writeString(testCode, "defaultContId",
0468:                            defaultContId); //NOI18N         
0469:                    testCode.add("Point hotspot = new Point("
0470:                            + new Double(hotspot.getX()).intValue() + "," + //NOI18N
0471:                            new Double(hotspot.getY()).intValue() + ");"); //NOI18N
0472:                    testCode
0473:                            .add("ld.startAdding(comps, bounds, hotspot, defaultContId);"); //NOI18N
0474:                    testCode.add("}"); //NOI18N
0475:                }
0476:                if (defaultContId != null) {
0477:                    setDragTarget(
0478:                            layoutModel.getLayoutComponent(defaultContId),
0479:                            comps, false);
0480:                }
0481:                if (logTestCode()) {
0482:                    testCode.add("// < START ADDING"); //NOI18N
0483:                }
0484:            }
0485:
0486:            public void startMoving(String[] compIds, Rectangle[] bounds,
0487:                    Point hotspot) {
0488:                if (logTestCode()) {
0489:                    testCode.add("// > START MOVING"); //NOI18N
0490:                }
0491:
0492:                LayoutComponent[] comps = new LayoutComponent[compIds.length];
0493:                for (int i = 0; i < compIds.length; i++) {
0494:                    comps[i] = layoutModel.getLayoutComponent(compIds[i]);
0495:                }
0496:                prepareDragger(comps, bounds, hotspot, LayoutDragger.ALL_EDGES);
0497:
0498:                if (logTestCode()) {
0499:                    testCode.add("{"); //NOI18N
0500:                    LayoutTestUtils.writeStringArray(testCode, "compIds",
0501:                            compIds); //NOI18N
0502:                    LayoutTestUtils.writeRectangleArray(testCode, "bounds",
0503:                            bounds); //NOI18N
0504:                    testCode.add("Point hotspot = new Point("
0505:                            + new Double(hotspot.getX()).intValue() + "," + //NOI18N
0506:                            new Double(hotspot.getY()).intValue() + ");"); //NOI18N
0507:                    testCode.add("ld.startMoving(compIds, bounds, hotspot);"); //NOI18N
0508:                    testCode.add("}"); //NOI18N
0509:                }
0510:
0511:                setDragTarget(comps[0].getParent(), comps, false);
0512:
0513:                if (logTestCode()) {
0514:                    testCode.add("// < START MOVING"); //NOI18N
0515:                }
0516:            }
0517:
0518:            // [change to one component only?]
0519:            public void startResizing(String[] compIds, Rectangle[] bounds,
0520:                    Point hotspot, int[] resizeEdges, boolean inLayout) {
0521:                if (logTestCode()) {
0522:                    testCode.add("// > START RESIZING"); //NOI18N
0523:                }
0524:
0525:                LayoutComponent[] comps = new LayoutComponent[compIds.length];
0526:                for (int i = 0; i < compIds.length; i++) {
0527:                    comps[i] = layoutModel.getLayoutComponent(compIds[i]);
0528:                }
0529:
0530:                int[] edges = new int[DIM_COUNT];
0531:                for (int i = 0; i < DIM_COUNT; i++) {
0532:                    edges[i] = resizeEdges[i] == LEADING
0533:                            || resizeEdges[i] == TRAILING ? resizeEdges[i]
0534:                            : LayoutRegion.NO_POINT;
0535:                }
0536:
0537:                prepareDragger(comps, bounds, hotspot, edges);
0538:
0539:                if (logTestCode()) {
0540:                    testCode.add("{"); //NOI18N
0541:                    LayoutTestUtils.writeStringArray(testCode, "compIds",
0542:                            compIds); //NOI18N
0543:                    LayoutTestUtils.writeRectangleArray(testCode, "bounds",
0544:                            bounds); //NOI18N
0545:                    testCode.add("Point hotspot = new Point("
0546:                            + new Double(hotspot.getX()).intValue() + "," + //NOI18N
0547:                            new Double(hotspot.getY()).intValue() + ");"); //NOI18N
0548:                    LayoutTestUtils.writeIntArray(testCode, "resizeEdges",
0549:                            resizeEdges); //NOI18N
0550:                    testCode.add("boolean inLayout = " + inLayout + ";"); // NOI18N
0551:                    testCode
0552:                            .add("ld.startResizing(compIds, bounds, hotspot, resizeEdges, inLayout);"); //NOI18N
0553:                    testCode.add("}"); //NOI18N
0554:                }
0555:
0556:                if (inLayout) {
0557:                    setDragTarget(comps[0].getParent(), comps, true);
0558:                } else {
0559:                    setDragTarget(null, null, true);
0560:                }
0561:
0562:                if (logTestCode()) {
0563:                    testCode.add("// < START RESIZING"); //NOI18N
0564:                }
0565:            }
0566:
0567:            private void prepareDragger(LayoutComponent[] comps,
0568:                    Rectangle[] bounds, Point hotspot, int[] edges) {
0569:                if (comps.length != bounds.length)
0570:                    throw new IllegalArgumentException();
0571:
0572:                LayoutRegion[] movingFormation = new LayoutRegion[bounds.length];
0573:                for (int i = 0; i < bounds.length; i++) {
0574:                    int baseline = visualMapper.getBaselinePosition(comps[i]
0575:                            .getId(), bounds[i].width, bounds[i].height);
0576:                    int baselinePos = baseline > 0 ? bounds[i].y + baseline
0577:                            : LayoutRegion.UNKNOWN;
0578:                    movingFormation[i] = new LayoutRegion();
0579:                    movingFormation[i].set(bounds[i], baselinePos);
0580:                }
0581:
0582:                dragger = new LayoutDragger(comps, movingFormation, new int[] {
0583:                        hotspot.x, hotspot.y }, edges, visualMapper);
0584:            }
0585:
0586:            /**
0587:             * @param p mouse cursor position in coordinates of the whole design area;
0588:             *        it is adjusted if the position is changed (due to a snap effect)
0589:             * @param containerId for container the cursor is currently moved over,
0590:             *        can be null if e.g. a root container is resized
0591:             * @param autoPositioning if true, searching for optimal position will be
0592:             *        performed - a found position will be painted and the moving
0593:             *        component snapped to it
0594:             * @param lockDimension if true, one dimension is locked for this move
0595:             *        (does not change); the dimension to lock must have aligned
0596:             *        position found in previous move steps - if this is true for both
0597:             *        dimensions then the one with smaller delta is chosen
0598:             * @param bounds (output) bounds of moving components after the move
0599:             */
0600:            public void move(Point p, String containerId,
0601:                    boolean autoPositioning, boolean lockDimension,
0602:                    Rectangle[] bounds) {
0603:
0604:                int x = (p != null) ? p.x : 0;
0605:                int y = (p != null) ? p.y : 0;
0606:
0607:                if (logTestCode()) {
0608:                    // this terrible code here is to store only two last move() calls
0609:                    if (!isMoving) {
0610:                        isMoving = true;
0611:                        // backup all current entries and clear the testcode list
0612:                        beforeMove = new ArrayList<String>();
0613:                        beforeMove.addAll(testCode);
0614:                        testCode = new ArrayList<String>();
0615:                        lastMovePoint = new Point(0, 0);
0616:                    }
0617:
0618:                    if (!((x == lastMovePoint.x) && (y == lastMovePoint.y))) {
0619:                        lastMovePoint = new Point(x, y);
0620:                        move1 = move2;
0621:                        testCode0 = testCode;
0622:                    }
0623:
0624:                    move2 = new ArrayList<String>();
0625:                    move2.add("// > MOVE");
0626:                    testCode = new ArrayList<String>();
0627:                }
0628:                if ((!visualStateUpToDate) || (dragger == null)) {
0629:                    return; // visual state of layout structure not updated yet (from last operation)
0630:                }
0631:
0632:                if (!dragger.isResizing()
0633:                        && (!lockDimension || dragger.getTargetContainer() == null)) {
0634:                    setDragTarget(layoutModel.getLayoutComponent(containerId),
0635:                            dragger.getMovingComponents(), false);
0636:                }
0637:
0638:                cursorPos[HORIZONTAL] = p.x;
0639:                cursorPos[VERTICAL] = p.y;
0640:
0641:                dragger.move(cursorPos, autoPositioning, lockDimension);
0642:
0643:                p.x = cursorPos[HORIZONTAL];
0644:                p.y = cursorPos[VERTICAL];
0645:
0646:                if (bounds != null) {
0647:                    LayoutRegion[] current = dragger.getMovingBounds();
0648:                    for (int i = 0; i < current.length; i++) {
0649:                        current[i].toRectangle(bounds[i]);
0650:                    }
0651:                }
0652:
0653:                if (logTestCode()) {
0654:                    move2.add("{"); //NOI18N
0655:                    move2.add("Point p = new Point(" + x + "," + y + ");"); //NOI18N
0656:                    LayoutTestUtils.writeString(move2, "containerId",
0657:                            containerId); //NOI18N
0658:                    move2.add("boolean autoPositioning = " + autoPositioning
0659:                            + ";"); //NOI18N
0660:                    move2.add("boolean lockDimension = " + lockDimension + ";"); //NOI18N
0661:                    LayoutTestUtils
0662:                            .writeRectangleArray(move2, "bounds", bounds); //NOI18N
0663:                    move2
0664:                            .add("ld.move(p, containerId, autoPositioning, lockDimension, bounds);"); //NOI18N
0665:                    move2.add("}"); //NOI18N
0666:                    move2.add("// < MOVE"); //NOI18N
0667:                }
0668:            }
0669:
0670:            private void setDragTarget(LayoutComponent targetContainer,
0671:                    LayoutComponent[] movingComps, boolean resizing) {
0672:                LayoutComponent prevContainer = dragger.getTargetContainer();
0673:                LayoutInterval[] roots;
0674:                if (targetContainer != null) {
0675:                    roots = resizing && movingComps.length > 0 ? movingComps[0]
0676:                            .getParentRoots()
0677:                            : getActiveLayoutRoots(targetContainer);
0678:                } else {
0679:                    roots = null;
0680:                }
0681:                dragger.setTargetContainer(targetContainer, roots);
0682:
0683:                if (prevContainer != targetContainer) {
0684:                    updateDraggingVisibility(prevContainer, movingComps,
0685:                            resizing, false);
0686:                    updateDraggingVisibility(targetContainer, movingComps,
0687:                            resizing, true);
0688:                }
0689:            }
0690:
0691:            // temporarily hide components from other layers when dragging in a container
0692:            private void updateDraggingVisibility(LayoutComponent container,
0693:                    LayoutComponent[] movingComps, boolean resizing,
0694:                    boolean draggingIn) {
0695:                if (container != null) {
0696:                    LayoutInterval[] targetRoots = resizing
0697:                            && movingComps.length > 0 ? movingComps[0]
0698:                            .getParentRoots() : getActiveLayoutRoots(container);
0699:                    for (LayoutComponent comp : container.getSubcomponents()) {
0700:                        for (LayoutComponent m : movingComps) {
0701:                            if (m == comp) {
0702:                                comp = null;
0703:                                break;
0704:                            }
0705:                        }
0706:                        if (comp != null
0707:                                && LayoutInterval.getRoot(comp
0708:                                        .getLayoutInterval(HORIZONTAL)) != targetRoots[HORIZONTAL]) {
0709:                            visualMapper.setComponentVisibility(comp.getId(),
0710:                                    !draggingIn);
0711:                        }
0712:                    }
0713:                }
0714:            }
0715:
0716:            public void endMoving(boolean committed) {
0717:                if (!committed && dragger == null)
0718:                    return; // redundant call
0719:
0720:                if (logTestCode()) {
0721:                    if (committed) {
0722:                        beforeMove.addAll(testCode0);
0723:                        beforeMove.addAll(move1);
0724:                        beforeMove.addAll(testCode);
0725:                        beforeMove.addAll(move2);
0726:                        testCode = beforeMove;
0727:                    }
0728:                    testCode.add("// > END MOVING"); //NOI18N
0729:                    isMoving = false;
0730:                }
0731:                try {
0732:                    LayoutComponent targetContainer = dragger
0733:                            .getTargetContainer();
0734:                    LayoutComponent[] components = dragger
0735:                            .getMovingComponents();
0736:                    updateDraggingVisibility(targetContainer, components,
0737:                            dragger.isResizing(), false);
0738:
0739:                    if (committed) {
0740:                        if (targetContainer != null) {
0741:                            boolean newComponents = components[0].getParent() == null;
0742:                            // determine the interval to add in each dimension
0743:                            LayoutInterval[] addingInts;
0744:                            if (components.length > 1) {
0745:                                if (newComponents) { // adding multiple new components (not in layout)
0746:                                    // (special case - moving multiple components from another layout)
0747:                                    LayoutRegion movingSpace = dragger
0748:                                            .getMovingSpace();
0749:                                    int dx = movingSpace.positions[HORIZONTAL][LEADING];
0750:                                    int dy = movingSpace.positions[HORIZONTAL][LEADING];
0751:                                    LayoutRegion[] movingBounds = dragger
0752:                                            .getMovingBounds();
0753:                                    Map<LayoutComponent, Rectangle> compToRect = new HashMap<LayoutComponent, Rectangle>();
0754:                                    for (int i = 0; i < components.length; i++) {
0755:                                        Rectangle r = movingBounds[i]
0756:                                                .toRectangle(new Rectangle());
0757:                                        r.x -= dx;
0758:                                        r.y -= dy;
0759:                                        compToRect.put(components[i], r);
0760:                                    }
0761:                                    addingInts = LayoutModel
0762:                                            .createIntervalsFromBounds(compToRect);
0763:                                } else { // moving multiple existing components (already in layout, no resizing)
0764:                                    LayoutInterval[] commonParents = new LayoutInterval[DIM_COUNT];
0765:                                    Map<LayoutComponent, LayoutComponent> compMap = new HashMap<LayoutComponent, LayoutComponent>();
0766:                                    LayoutRegion origSpace = new LayoutRegion();
0767:                                    for (LayoutComponent comp : components) {
0768:                                        for (int dim = 0; dim < DIM_COUNT; dim++) {
0769:                                            if (commonParents[dim] == null) {
0770:                                                commonParents[dim] = comp
0771:                                                        .getLayoutInterval(dim);
0772:                                            } else {
0773:                                                commonParents[dim] = LayoutInterval
0774:                                                        .getCommonParent(
0775:                                                                commonParents[dim],
0776:                                                                comp
0777:                                                                        .getLayoutInterval(dim));
0778:                                            }
0779:                                        }
0780:                                        compMap.put(comp, comp); // moving the same components
0781:                                        origSpace.expand(comp
0782:                                                .getLayoutInterval(HORIZONTAL)
0783:                                                .getCurrentSpace());
0784:                                    }
0785:                                    addingInts = new LayoutInterval[DIM_COUNT];
0786:                                    for (int dim = 0; dim < DIM_COUNT; dim++) {
0787:                                        addingInts[dim] = restrictedCopy(
0788:                                                commonParents[dim], compMap,
0789:                                                origSpace, dim, null);
0790:                                    }
0791:                                    // component intervals were moved to the new enclosing group,
0792:                                    // so also remove the components from their container
0793:                                    for (LayoutComponent comp : components) {
0794:                                        layoutModel
0795:                                                .removeComponent(comp, false);
0796:                                    }
0797:                                }
0798:                            } else { // one component (adding/moving/resizing)
0799:                                addingInts = new LayoutInterval[DIM_COUNT];
0800:                                LayoutComponent comp = components[0];
0801:                                for (int dim = 0; dim < DIM_COUNT; dim++) {
0802:                                    addingInts[dim] = comp
0803:                                            .getLayoutInterval(dim);
0804:                                }
0805:                            }
0806:
0807:                            if (newComponents) { // ensure the interval size matches the real component size
0808:                                LayoutRegion[] movingBounds = dragger
0809:                                        .getMovingBounds();
0810:                                for (int i = 0; i < components.length; i++) {
0811:                                    LayoutComponent comp = components[i];
0812:                                    Dimension preferred = visualMapper
0813:                                            .getComponentPreferredSize(comp
0814:                                                    .getId());
0815:                                    for (int dim = 0; dim < DIM_COUNT; dim++) {
0816:                                        int size = movingBounds[i].size(dim);
0817:                                        if (preferred == null
0818:                                                || size != ((dim == HORIZONTAL) ? preferred.width
0819:                                                        : preferred.height)) {
0820:                                            comp.getLayoutInterval(dim)
0821:                                                    .setPreferredSize(size);
0822:                                        }
0823:                                    }
0824:                                }
0825:                            }
0826:
0827:                            addComponents(components, targetContainer,
0828:                                    addingInts, newComponents);
0829:                        } else { // resizing root container
0830:                            assert dragger.isResizing();
0831:
0832:                            modelListener.deactivate(); // do not react on model changes
0833:
0834:                            LayoutRegion space = dragger.getMovingBounds()[0];
0835:                            for (int dim = 0; dim < DIM_COUNT; dim++) {
0836:                                components[0].getLayoutInterval(dim)
0837:                                        .setCurrentSpace(space);
0838:                            }
0839:                            if (components[0].isLayoutContainer()) {
0840:                                imposeCurrentContainerSize(components[0],
0841:                                        dragger.getSizes(), true);
0842:                            }
0843:                        }
0844:
0845:                        if (dragger.isResizing()
0846:                                && components[0].isLayoutContainer())
0847:                            updateDesignModifications(components[0]);
0848:
0849:                        visualStateUpToDate = false;
0850:                    }
0851:                } finally {
0852:                    dragger = null;
0853:                    if (logTestCode()) {
0854:                        testCode.add("ld.endMoving(" + committed + ");"); //NOI18N
0855:                        testCode.add("// < END MOVING"); //NOI18N
0856:                    }
0857:                }
0858:            }
0859:
0860:            private void addComponents(LayoutComponent[] components,
0861:                    LayoutComponent targetContainer,
0862:                    LayoutInterval[] addingInts, boolean freshComponents) {
0863:                try {
0864:                    LayoutFeeder layoutFeeder = new LayoutFeeder(operations,
0865:                            dragger, addingInts);
0866:
0867:                    // remove the components from original location if still there
0868:                    // (in case of resizing the feeder needed to know the original positions)
0869:                    for (LayoutComponent comp : components) {
0870:                        if (comp.getParent() != null) {
0871:                            if (dragger.isResizing(HORIZONTAL)) {
0872:                                layoutModel.removeComponentFromLinkSizedGroup(
0873:                                        comp, HORIZONTAL);
0874:                            }
0875:                            if (dragger.isResizing(VERTICAL)) {
0876:                                layoutModel.removeComponentFromLinkSizedGroup(
0877:                                        comp, VERTICAL);
0878:                            }
0879:                            layoutModel
0880:                                    .removeComponentAndIntervals(comp, false);
0881:                        }
0882:                    }
0883:
0884:                    modelListener.deactivate(); // do not react on model changes during adding
0885:
0886:                    // add the components to the model (to the target container)
0887:                    for (LayoutComponent comp : components) {
0888:                        layoutModel.addComponent(comp, targetContainer, -1);
0889:                    }
0890:
0891:                    // add the components' intervals
0892:                    layoutFeeder.add();
0893:                    if (layoutFeeder.imposeSize) {
0894:                        imposeSize = true;
0895:                    }
0896:                    if (layoutFeeder.optimizeStructure) {
0897:                        optimizeStructure = true;
0898:                    } // if an overlap occurred we can't calculate the correct sizes
0899:                    // of resizing intervals, thus need to do real layout first (to
0900:                    // get the right picture), then update the actual sizes, and then
0901:                    // re-layout with design specific attributes (container resizing gap)
0902:
0903:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
0904:                        destroyGroupIfRedundant(addingInts[dim],
0905:                                addingInts[dim].getParent());
0906:                    }
0907:
0908:                    for (LayoutComponent comp : components) {
0909:                        if ((freshComponents || dragger.isResizing())
0910:                                && comp.isLayoutContainer()) {
0911:                            // container size needs to be defined from inside before the layout is built
0912:                            imposeCurrentContainerSize(components[0], dragger
0913:                                    .getSizes(), true);
0914:                        } else if (dragger.isResizing()) {
0915:                            // component might be resized to default size
0916:                            for (int i = 0; i < DIM_COUNT; i++) {
0917:                                if (dragger.snappedToDefaultSize(i))
0918:                                    operations.resizeInterval(comp
0919:                                            .getLayoutInterval(i),
0920:                                            NOT_EXPLICITLY_DEFINED);
0921:                            }
0922:                        }
0923:                    }
0924:
0925:                    updateDesignModifications(targetContainer);
0926:                } finally {
0927:                    if (!modelListener.isActive()) {
0928:                        modelListener.activate();
0929:                    }
0930:                }
0931:            }
0932:
0933:            private void addUnspecified(LayoutComponent[] components,
0934:                    LayoutComponent targetContainer, LayoutInterval[] addingInts) {
0935:                // add the components to the model (to the target container)
0936:                for (LayoutComponent comp : components) {
0937:                    layoutModel.addComponent(comp, targetContainer, -1);
0938:                }
0939:                LayoutInterval[] targetRoots = layoutModel
0940:                        .addNewLayoutRoots(targetContainer);
0941:                for (int dim = 0; dim < DIM_COUNT; dim++) {
0942:                    LayoutInterval interval = addingInts[dim];
0943:                    LayoutInterval seq = interval.isSequential() ? interval
0944:                            : new LayoutInterval(SEQUENTIAL);
0945:                    LayoutInterval gap = new LayoutInterval(SINGLE);
0946:                    boolean resizing = LayoutInterval.wantResize(interval);
0947:                    if (!resizing) {
0948:                        gap.setSizes(0, 0, Short.MAX_VALUE);
0949:                    }
0950:                    seq.add(gap, 0);
0951:                    if (interval != seq) {
0952:                        seq.add(interval, -1);
0953:                        interval.setAlignment(DEFAULT);
0954:                    }
0955:                    gap = new LayoutInterval(SINGLE);
0956:                    if (!resizing) {
0957:                        gap.setSizes(0, 0, Short.MAX_VALUE);
0958:                    }
0959:                    seq.add(gap, -1);
0960:                    targetRoots[dim].add(seq, -1);
0961:                }
0962:            }
0963:
0964:            private void addToEmpty(LayoutComponent[] components,
0965:                    LayoutComponent targetContainer, LayoutInterval[] addingInts) {
0966:                assert targetContainer.getSubComponentCount() == 0;
0967:                for (LayoutComponent comp : components) {
0968:                    layoutModel.addComponent(comp, targetContainer, -1);
0969:                }
0970:                LayoutInterval[] roots = getActiveLayoutRoots(targetContainer);
0971:                for (int dim = 0; dim < DIM_COUNT; dim++) {
0972:                    LayoutInterval root = roots[dim];
0973:                    assert root.isParallel();
0974:                    LayoutInterval interval = addingInts[dim];
0975:                    if (!interval.isParallel()) {
0976:                        layoutModel.addInterval(interval, root, -1);
0977:                    } else {
0978:                        while (interval.getSubIntervalCount() > 0) {
0979:                            layoutModel.addInterval(interval.remove(0), root,
0980:                                    -1);
0981:                        }
0982:                    }
0983:                }
0984:            }
0985:
0986:            /**
0987:             * Creates copy of the given interval restricted to specified components
0988:             * and region (<code>space</code>).
0989:             *
0990:             * @param interval interval whose restricted copy should be created.
0991:             * @param componentMap components that determine the restriction.
0992:             * @param space region (current space) that determine the restriction.
0993:             * @param dimension dimension that restricted layout interval belongs to.
0994:             * @param temp internal helper parameter for recursive invocation.
0995:             * Pass <code>null</code> when you invoke this method.
0996:             */
0997:            private LayoutInterval restrictedCopy(LayoutInterval interval,
0998:                    Map<LayoutComponent, LayoutComponent> componentMap,
0999:                    LayoutRegion space, int dimension, List<Object> temp) {
1000:                boolean processTemp = (temp == null);
1001:                if (temp == null) {
1002:                    temp = new LinkedList<Object>();
1003:                }
1004:                if (interval.isGroup()) {
1005:                    boolean parallel = interval.isParallel();
1006:                    LayoutInterval copy = LayoutInterval.cloneInterval(
1007:                            interval, null);
1008:                    Iterator iter = interval.getSubIntervals();
1009:                    int compCount = 0; // Number of components already copied to the group
1010:                    boolean includeGap = false; // Helper variables that allow us to insert gaps
1011:                    int firstGapToInclude = 0; // instead of components that has been filtered out.
1012:                    int gapStart = interval.getCurrentSpace().positions[dimension][LEADING];
1013:                    while (iter.hasNext()) {
1014:                        LayoutInterval sub = (LayoutInterval) iter.next();
1015:                        LayoutInterval subCopy = restrictedCopy(sub,
1016:                                componentMap, space, dimension, temp);
1017:                        if (subCopy != null) {
1018:                            if (!sub.isEmptySpace()) {
1019:                                if (includeGap) {
1020:                                    gapStart = Math
1021:                                            .max(
1022:                                                    space.positions[dimension][LEADING],
1023:                                                    gapStart);
1024:                                    int size = sub.getCurrentSpace().positions[dimension][LEADING]
1025:                                            - gapStart;
1026:                                    integrateGap(copy, size, firstGapToInclude);
1027:                                    includeGap = false;
1028:                                }
1029:                                gapStart = sub.getCurrentSpace().positions[dimension][TRAILING];
1030:                                firstGapToInclude = copy.getSubIntervalCount();
1031:                            }
1032:                            if (sub.isComponent()) {
1033:                                // Remember where to add component intervals - they cannot
1034:                                // be moved immediately because the model listener would
1035:                                // destroy the adjacent intervals before we would be able
1036:                                // to copy them.
1037:                                temp.add(subCopy);
1038:                                temp.add(copy);
1039:                                temp
1040:                                        .add(new Integer(subCopy
1041:                                                .getRawAlignment()));
1042:                                temp.add(new Integer(copy.getSubIntervalCount()
1043:                                        + compCount));
1044:                                compCount++;
1045:                            } else {
1046:                                layoutModel.addInterval(subCopy, copy, -1);
1047:                            }
1048:                        } else {
1049:                            if (!parallel) {
1050:                                includeGap = true;
1051:                            }
1052:                        }
1053:                    }
1054:                    if (includeGap) {
1055:                        gapStart = Math.max(
1056:                                space.positions[dimension][LEADING], gapStart);
1057:                        int gapEnd = Math
1058:                                .min(
1059:                                        space.positions[dimension][TRAILING],
1060:                                        interval.getCurrentSpace().positions[dimension][TRAILING]);
1061:                        integrateGap(copy, gapEnd - gapStart, firstGapToInclude);
1062:                    }
1063:                    if (copy.getSubIntervalCount() + compCount > 0) {
1064:                        if (processTemp) {
1065:                            // Insert component intervals
1066:                            iter = temp.iterator();
1067:                            while (iter.hasNext()) {
1068:                                LayoutInterval comp = (LayoutInterval) iter
1069:                                        .next();
1070:                                LayoutInterval parent = (LayoutInterval) iter
1071:                                        .next();
1072:                                int alignment = ((Integer) iter.next())
1073:                                        .intValue();
1074:                                int index = ((Integer) iter.next()).intValue();
1075:                                if (comp.getParent() != null) { // component reused - not copied, just moved
1076:                                    layoutModel.removeInterval(comp);
1077:                                }
1078:                                layoutModel.setIntervalAlignment(comp,
1079:                                        alignment);
1080:                                layoutModel.addInterval(comp, parent, index);
1081:                            }
1082:                            // Consolidate the groups where the components has been added
1083:                            boolean active = modelListener.isActive();
1084:                            if (active)
1085:                                modelListener.deactivate();
1086:                            iter = temp.iterator();
1087:                            while (iter.hasNext()) {
1088:                                iter.next(); // skip the component
1089:                                LayoutInterval group = (LayoutInterval) iter
1090:                                        .next();
1091:                                iter.next();
1092:                                iter.next(); // skip alignment and index
1093:                                LayoutInterval parent = group.getParent();
1094:                                while (group.getSubIntervalCount() == 1
1095:                                        && parent != null) {
1096:                                    LayoutInterval sub = group
1097:                                            .getSubInterval(0);
1098:                                    layoutModel.removeInterval(sub);
1099:                                    int alignment = group.getAlignment();
1100:                                    int index = layoutModel
1101:                                            .removeInterval(group);
1102:                                    layoutModel.setIntervalAlignment(sub,
1103:                                            alignment);
1104:                                    layoutModel.addInterval(sub, parent, index);
1105:                                    group = sub;
1106:                                }
1107:                            }
1108:                            compCount = 0;
1109:                            if (active)
1110:                                modelListener.activate();
1111:                        }
1112:                        // consolidate copy
1113:                        if ((copy.getSubIntervalCount() == 1)
1114:                                && (compCount == 0)) {
1115:                            boolean active = modelListener.isActive();
1116:                            if (active)
1117:                                modelListener.deactivate();
1118:                            LayoutInterval subCopy = copy.getSubInterval(0);
1119:                            layoutModel.removeInterval(subCopy);
1120:                            layoutModel.setIntervalAlignment(subCopy, copy
1121:                                    .getAlignment());
1122:                            if (copy.isSequential() && subCopy.isEmptySpace()) {
1123:                                copy = null;
1124:                            } else {
1125:                                copy = subCopy;
1126:                            }
1127:                            if (active)
1128:                                modelListener.activate();
1129:                        }
1130:                        return copy;
1131:                    } else {
1132:                        return null;
1133:                    }
1134:                } else if (interval.isComponent()) {
1135:                    LayoutComponent comp = componentMap.get(interval
1136:                            .getComponent());
1137:                    if (comp != null) {
1138:                        if (comp != interval.getComponent()) { // there is a copied component available
1139:                            interval = LayoutInterval.cloneInterval(interval,
1140:                                    comp.getLayoutInterval(dimension));
1141:                        } // otherwise the component will be moved from its original location
1142:                        return interval;
1143:                    } else { // skip this component
1144:                        return null;
1145:                    }
1146:                } else {
1147:                    assert interval.isEmptySpace();
1148:                    int[] bounds = emptySpaceBounds(interval, dimension);
1149:                    int rangeStart = space.positions[dimension][LEADING];
1150:                    int rangeEnd = space.positions[dimension][TRAILING];
1151:                    if ((bounds[0] < rangeEnd) && (bounds[1] > rangeStart)) {
1152:                        LayoutInterval gap = new LayoutInterval(SINGLE);
1153:                        gap.setAttributes(interval.getAttributes());
1154:                        if ((bounds[0] < rangeStart) || (bounds[1] > rangeEnd)) {
1155:                            // Partial overlap with the provides space
1156:                            int min = interval.getMinimumSize();
1157:                            if (min >= 0)
1158:                                min = USE_PREFERRED_SIZE;
1159:                            int pref = Math.min(bounds[1], rangeEnd)
1160:                                    - Math.max(bounds[0], rangeStart);
1161:                            int max = interval.getMaximumSize();
1162:                            if (max >= 0)
1163:                                max = USE_PREFERRED_SIZE;
1164:                            gap.setSizes(min, pref, max);
1165:                        } else {
1166:                            gap.setSizes(interval.getMinimumSize(), interval
1167:                                    .getPreferredSize(), interval
1168:                                    .getMaximumSize());
1169:                        }
1170:                        return gap;
1171:                    } else {
1172:                        // Outside the provided space
1173:                        return null;
1174:                    }
1175:                }
1176:            }
1177:
1178:            /**
1179:             * Helper method used by <code>restrictedCopy()</code> method.
1180:             * Replaces empty spaces at the end of the sequential group
1181:             * by an empty space of the specified size. Only empty spaces
1182:             * with index >= boundary are replaced.
1183:             *
1184:             * @param seqGroup sequential group.
1185:             * @param size size of the empty space that should be added.
1186:             * @param boundary index in the sequential group that limits
1187:             * the replacement of the empty spaces.
1188:             */
1189:            private void integrateGap(LayoutInterval seqGroup, int size,
1190:                    int boundary) {
1191:                while ((seqGroup.getSubIntervalCount() > boundary)
1192:                        && seqGroup.getSubInterval(
1193:                                seqGroup.getSubIntervalCount() - 1)
1194:                                .isEmptySpace()) {
1195:                    layoutModel.removeInterval(seqGroup.getSubInterval(seqGroup
1196:                            .getSubIntervalCount() - 1));
1197:                }
1198:                if (size > 0) {
1199:                    LayoutInterval gap = new LayoutInterval(SINGLE);
1200:                    gap.setSize(size);
1201:                    layoutModel.addInterval(gap, seqGroup, -1);
1202:                }
1203:            }
1204:
1205:            /**
1206:             * Returns bounds (e.g. current space) of the empty space in the given dimension.
1207:             *
1208:             * @param emptySpace empty space.
1209:             * @param dimenion dimension.
1210:             * @return array whose the first item is the leading bound of the empty
1211:             * space and the second item is the trailing bound.
1212:             */
1213:            private int[] emptySpaceBounds(LayoutInterval emptySpace,
1214:                    int dimension) {
1215:                assert emptySpace.isEmptySpace();
1216:                int leading, trailing;
1217:                LayoutInterval parent = emptySpace.getParent();
1218:                int index = parent.indexOf(emptySpace);
1219:                if (index == 0) {
1220:                    leading = parent.getCurrentSpace().positions[dimension][LEADING];
1221:                } else {
1222:                    leading = parent.getSubInterval(index - 1)
1223:                            .getCurrentSpace().positions[dimension][TRAILING];
1224:                }
1225:                if (index + 1 == parent.getSubIntervalCount()) {
1226:                    trailing = parent.getCurrentSpace().positions[dimension][TRAILING];
1227:                } else {
1228:                    trailing = parent.getSubInterval(index + 1)
1229:                            .getCurrentSpace().positions[dimension][LEADING];
1230:                }
1231:                return new int[] { leading, trailing };
1232:            }
1233:
1234:            /**
1235:             * Removes currently dragged components from layout model. Called when
1236:             * the components were dragged out of the form (or to a container not
1237:             * managed by this layout model).
1238:             */
1239:            public void removeDraggedComponents() {
1240:                if (dragger != null) {
1241:                    LayoutComponent[] components = dragger
1242:                            .getMovingComponents();
1243:                    for (int i = 0; i < components.length; i++) {
1244:                        layoutModel.removeComponentAndIntervals(components[i],
1245:                                !components[i].isLayoutContainer());
1246:                    }
1247:                    endMoving(false);
1248:                }
1249:            }
1250:
1251:            public void paintMoveFeedback(Graphics2D g) {
1252:                if (dragger != null) { // Dragger might not be initialized yet
1253:                    dragger.paintMoveFeedback(g);
1254:                }
1255:            }
1256:
1257:            /**
1258:             * Paints layout information (alignment) for the selected component.
1259:             *
1260:             * @param g graphics object to use.
1261:             * @param componentId ID of selected component.
1262:             */
1263:            public void paintSelection(Graphics2D g, String componentId) {
1264:                LayoutComponent comp = layoutModel
1265:                        .getLayoutComponent(componentId);
1266:                if ((comp != null) && (comp.getParent() != null)) {
1267:                    paintSelection(g, comp, HORIZONTAL);
1268:                    paintSelection(g, comp, VERTICAL);
1269:                }
1270:            }
1271:
1272:            /**
1273:             * Paints layout information (alignment) for the selected component
1274:             * and specified dimension.
1275:             *
1276:             * @param g graphics object to use.
1277:             * @param component selected layout component.
1278:             * @param dimension dimension whose layout should be visualized.
1279:             */
1280:            private void paintSelection(Graphics2D g,
1281:                    LayoutComponent component, int dimension) {
1282:                LayoutInterval interval = component
1283:                        .getLayoutInterval(dimension);
1284:                if (component.isLinkSized(HORIZONTAL)
1285:                        || component.isLinkSized(VERTICAL)) {
1286:                    paintLinks(g, component);
1287:                }
1288:                // Paint baseline alignment
1289:                if (interval.getAlignment() == BASELINE) {
1290:                    LayoutInterval alignedParent = interval.getParent();
1291:                    int oppDimension = (dimension == HORIZONTAL) ? VERTICAL
1292:                            : HORIZONTAL;
1293:                    LayoutRegion region = alignedParent.getCurrentSpace();
1294:                    int x = region.positions[dimension][BASELINE];
1295:                    int y1 = region.positions[oppDimension][LEADING];
1296:                    int y2 = region.positions[oppDimension][TRAILING];
1297:                    if ((y1 != LayoutRegion.UNKNOWN)
1298:                            && (y2 != LayoutRegion.UNKNOWN)) {
1299:                        if (dimension == HORIZONTAL) {
1300:                            g.drawLine(x, y1, x, y2);
1301:                        } else {
1302:                            g.drawLine(y1, x, y2, x);
1303:                        }
1304:                    }
1305:                }
1306:                int lastAlignment = -1;
1307:                while (interval.getParent() != null) {
1308:                    LayoutInterval parent = interval.getParent();
1309:                    if (parent.getType() == SEQUENTIAL) {
1310:                        int alignment = LayoutInterval
1311:                                .getEffectiveAlignment(interval);
1312:                        int index = parent.indexOf(interval);
1313:                        int start, end;
1314:                        switch (alignment) {
1315:                        case LEADING:
1316:                            start = 0;
1317:                            end = index;
1318:                            lastAlignment = LEADING;
1319:                            break;
1320:                        case TRAILING:
1321:                            start = index + 1;
1322:                            end = parent.getSubIntervalCount();
1323:                            lastAlignment = TRAILING;
1324:                            break;
1325:                        default:
1326:                            switch (lastAlignment) {
1327:                            case LEADING:
1328:                                start = 0;
1329:                                end = index;
1330:                                break;
1331:                            case TRAILING:
1332:                                start = index + 1;
1333:                                end = parent.getSubIntervalCount();
1334:                                break;
1335:                            default:
1336:                                start = 0;
1337:                                end = parent.getSubIntervalCount();
1338:                                break;
1339:                            }
1340:                        }
1341:                        for (int i = start; i < end; i++) {
1342:                            LayoutInterval candidate = parent.getSubInterval(i);
1343:                            if (candidate.isEmptySpace()) {
1344:                                paintAlignment(
1345:                                        g,
1346:                                        candidate,
1347:                                        dimension,
1348:                                        LayoutInterval
1349:                                                .getEffectiveAlignment(candidate));
1350:                            }
1351:                        }
1352:                    } else {
1353:                        int alignment = interval.getAlignment();
1354:                        if (!LayoutInterval.wantResizeInLayout(interval)) {
1355:                            lastAlignment = alignment;
1356:                        }
1357:                        paintAlignment(g, interval, dimension, lastAlignment);
1358:                    }
1359:                    interval = interval.getParent();
1360:                }
1361:            }
1362:
1363:            private void paintLinks(Graphics2D g, LayoutComponent component) {
1364:
1365:                if ((component.isLinkSized(HORIZONTAL))
1366:                        && (component.isLinkSized(VERTICAL))) {
1367:                    Map<Integer, List<String>> linkGroupsH = layoutModel
1368:                            .getLinkSizeGroups(HORIZONTAL);
1369:                    Map<Integer, List<String>> linkGroupsV = layoutModel
1370:                            .getLinkSizeGroups(VERTICAL);
1371:                    Integer linkIdH = new Integer(component
1372:                            .getLinkSizeId(HORIZONTAL));
1373:                    Integer linkIdV = new Integer(component
1374:                            .getLinkSizeId(VERTICAL));
1375:
1376:                    List<String> lH = linkGroupsH.get(linkIdH);
1377:                    List<String> lV = linkGroupsV.get(linkIdV);
1378:
1379:                    Set<String> merged = new HashSet<String>();
1380:                    for (int i = 0; i < lH.size(); i++) {
1381:                        merged.add(lH.get(i));
1382:                    }
1383:                    for (int i = 0; i < lV.size(); i++) {
1384:                        merged.add(lV.get(i));
1385:                    }
1386:
1387:                    Iterator<String> mergedIt = merged.iterator();
1388:                    while (mergedIt.hasNext()) {
1389:                        String id = mergedIt.next();
1390:                        LayoutComponent lc = layoutModel.getLayoutComponent(id);
1391:                        LayoutInterval interval = lc
1392:                                .getLayoutInterval(HORIZONTAL);
1393:                        LayoutRegion region = interval.getCurrentSpace();
1394:                        Image badge = null;
1395:                        if ((lV.contains(id)) && (lH.contains(id))) {
1396:                            badge = getLinkBadge(BOTH_DIMENSIONS);
1397:                        } else {
1398:                            if (lH.contains(lc.getId())) {
1399:                                badge = getLinkBadge(HORIZONTAL);
1400:                            }
1401:                            if (lV.contains(lc.getId())) {
1402:                                badge = getLinkBadge(VERTICAL);
1403:                            }
1404:                        }
1405:                        int x = region.positions[HORIZONTAL][TRAILING]
1406:                                - region.size(HORIZONTAL) / 4
1407:                                - (badge.getWidth(null) / 2);
1408:                        int y = region.positions[VERTICAL][LEADING]
1409:                                - (badge.getHeight(null));
1410:                        g.drawImage(badge, x, y, null);
1411:                    }
1412:                } else {
1413:                    int dimension = (component.isLinkSized(HORIZONTAL)) ? HORIZONTAL
1414:                            : VERTICAL;
1415:                    Map map = layoutModel.getLinkSizeGroups(dimension);
1416:
1417:                    Integer linkId = new Integer(component
1418:                            .getLinkSizeId(dimension));
1419:                    List l = (List) map.get(linkId);
1420:                    Iterator mergedIt = l.iterator();
1421:
1422:                    while (mergedIt.hasNext()) {
1423:                        String id = (String) mergedIt.next();
1424:                        LayoutComponent lc = layoutModel.getLayoutComponent(id);
1425:                        LayoutInterval interval = lc
1426:                                .getLayoutInterval(dimension);
1427:                        LayoutRegion region = interval.getCurrentSpace();
1428:                        Image badge = getLinkBadge(dimension);
1429:                        int x = region.positions[HORIZONTAL][TRAILING]
1430:                                - region.size(HORIZONTAL) / 4
1431:                                - (badge.getWidth(null) / 2);
1432:                        int y = region.positions[VERTICAL][LEADING]
1433:                                - (badge.getHeight(null));
1434:                        g.drawImage(badge, x, y, null);
1435:                    }
1436:                }
1437:            }
1438:
1439:            private Image linkBadgeBoth = null;
1440:            private Image linkBadgeHorizontal = null;
1441:            private Image linkBadgeVertical = null;
1442:
1443:            private static final int BOTH_DIMENSIONS = 2;
1444:
1445:            private Image getLinkBadge(int dimension) {
1446:                if (dimension == (BOTH_DIMENSIONS)) {
1447:                    if (linkBadgeBoth == null) {
1448:                        linkBadgeBoth = Utilities
1449:                                .loadImage("org/netbeans/modules/form/resources/sameboth.png"); //NOI18N
1450:                    }
1451:                    return linkBadgeBoth;
1452:                }
1453:                if (dimension == HORIZONTAL) {
1454:                    if (linkBadgeHorizontal == null) {
1455:                        linkBadgeHorizontal = Utilities
1456:                                .loadImage("org/netbeans/modules/form/resources/samewidth.png"); //NOI18N
1457:                    }
1458:                    return linkBadgeHorizontal;
1459:                }
1460:                if (dimension == VERTICAL) {
1461:                    if (linkBadgeVertical == null) {
1462:                        linkBadgeVertical = Utilities
1463:                                .loadImage("org/netbeans/modules/form/resources/sameheight.png"); //NOI18N
1464:                    }
1465:                    return linkBadgeVertical;
1466:                }
1467:                return null;
1468:            }
1469:
1470:            private void paintAlignment(Graphics2D g, LayoutInterval interval,
1471:                    int dimension, int alignment) {
1472:                LayoutInterval parent = interval.getParent();
1473:                boolean baseline = parent.isParallel()
1474:                        && (parent.getGroupAlignment() == BASELINE);
1475:                LayoutRegion group = parent.getCurrentSpace();
1476:                int opposite = (dimension == HORIZONTAL) ? VERTICAL
1477:                        : HORIZONTAL;
1478:                int x1, x2, y;
1479:                if (interval.isEmptySpace()) {
1480:                    int index = parent.indexOf(interval);
1481:                    int[] ya, yb;
1482:                    boolean x1group, x2group;
1483:                    if (index == 0) {
1484:                        x1 = group.positions[dimension][baseline ? BASELINE
1485:                                : LEADING];
1486:                        ya = visualIntervalPosition(parent, opposite, LEADING);
1487:                        x1group = LayoutInterval.getFirstParent(interval,
1488:                                PARALLEL).getParent() != null;
1489:                    } else {
1490:                        LayoutInterval x1int = parent.getSubInterval(index - 1);
1491:                        if (x1int.isParallel()
1492:                                && (x1int.getGroupAlignment() == BASELINE)) {
1493:                            x1 = x1int.getCurrentSpace().positions[dimension][BASELINE];
1494:                        } else {
1495:                            if (x1int.isEmptySpace())
1496:                                return;
1497:                            x1 = x1int.getCurrentSpace().positions[dimension][TRAILING];
1498:                        }
1499:                        ya = visualIntervalPosition(x1int, opposite, TRAILING);
1500:                        x1group = x1int.isGroup();
1501:                    }
1502:                    if (index + 1 == parent.getSubIntervalCount()) {
1503:                        x2 = group.positions[dimension][baseline ? BASELINE
1504:                                : TRAILING];
1505:                        yb = visualIntervalPosition(parent, opposite, TRAILING);
1506:                        x2group = LayoutInterval.getFirstParent(interval,
1507:                                PARALLEL).getParent() != null;
1508:                    } else {
1509:                        LayoutInterval x2int = parent.getSubInterval(index + 1);
1510:                        if (x2int.isParallel()
1511:                                && (x2int.getGroupAlignment() == BASELINE)) {
1512:                            x2 = x2int.getCurrentSpace().positions[dimension][BASELINE];
1513:                        } else {
1514:                            if (x2int.isEmptySpace())
1515:                                return;
1516:                            x2 = x2int.getCurrentSpace().positions[dimension][LEADING];
1517:                        }
1518:                        yb = visualIntervalPosition(x2int, opposite, LEADING);
1519:                        x2group = x2int.isGroup();
1520:                    }
1521:                    if ((x1 == LayoutRegion.UNKNOWN)
1522:                            || (x2 == LayoutRegion.UNKNOWN))
1523:                        return;
1524:                    int y1 = Math.min(ya[1], yb[1]);
1525:                    int y2 = Math.max(ya[0], yb[0]);
1526:                    y = (y1 + y2) / 2;
1527:                    if ((ya[1] < yb[0]) || (yb[1] < ya[0])) {
1528:                        // no intersection
1529:                        if (dimension == HORIZONTAL) {
1530:                            g.drawLine(x1, ya[0], x1, y);
1531:                            g.drawLine(x1, ya[0], x1, ya[1]);
1532:                            g.drawLine(x2, yb[0], x2, y);
1533:                            g.drawLine(x2, yb[0], x2, yb[1]);
1534:                        } else {
1535:                            g.drawLine(ya[0], x1, y, x1);
1536:                            g.drawLine(ya[0], x1, ya[1], x1);
1537:                            g.drawLine(yb[0], x2, y, x2);
1538:                            g.drawLine(yb[0], x2, yb[1], x2);
1539:                        }
1540:                    } else {
1541:                        if (dimension == HORIZONTAL) {
1542:                            if (x1group)
1543:                                g.drawLine(x1, ya[0], x1, ya[1]);
1544:                            if (x2group)
1545:                                g.drawLine(x2, yb[0], x2, yb[1]);
1546:                        } else {
1547:                            if (x1group)
1548:                                g.drawLine(ya[0], x1, ya[1], x1);
1549:                            if (x2group)
1550:                                g.drawLine(yb[0], x2, yb[1], x2);
1551:                        }
1552:                    }
1553:                } else {
1554:                    LayoutRegion child = interval.getCurrentSpace();
1555:                    if ((alignment == LEADING) || (alignment == TRAILING)) {
1556:                        x1 = group.positions[dimension][baseline ? BASELINE
1557:                                : alignment];
1558:                        if (interval.isParallel()
1559:                                && (interval.getAlignment() == BASELINE)) {
1560:                            x2 = child.positions[dimension][BASELINE];
1561:                        } else {
1562:                            x2 = child.positions[dimension][alignment];
1563:                        }
1564:                    } else {
1565:                        return;
1566:                    }
1567:                    if ((x1 == LayoutRegion.UNKNOWN)
1568:                            || (x2 == LayoutRegion.UNKNOWN))
1569:                        return;
1570:                    int[] pos = visualIntervalPosition(parent, opposite,
1571:                            alignment);
1572:                    y = (pos[0] + pos[1]) / 2;
1573:                    int xa = group.positions[dimension][LEADING];
1574:                    int xb = group.positions[dimension][TRAILING];
1575:                    if (parent.getParent() != null) {
1576:                        if (dimension == HORIZONTAL) {
1577:                            if (alignment == LEADING) {
1578:                                g.drawLine(xa, pos[0], xa, pos[1]);
1579:                            } else if (alignment == TRAILING) {
1580:                                g.drawLine(xb, pos[0], xb, pos[1]);
1581:                            }
1582:                        } else {
1583:                            if (alignment == LEADING) {
1584:                                g.drawLine(pos[0], xa, pos[1], xa);
1585:                            } else if (alignment == TRAILING) {
1586:                                g.drawLine(pos[0], xb, pos[1], xb);
1587:                            }
1588:                        }
1589:                    }
1590:                }
1591:                // Avoid overload of EQ when current space is incorrectly calculated.
1592:                if ((x2 - x1 > 1) && (Math.abs(y) <= Short.MAX_VALUE)
1593:                        && (Math.abs(x1) <= Short.MAX_VALUE)
1594:                        && (Math.abs(x2) <= Short.MAX_VALUE)) {
1595:                    int x, angle;
1596:                    if (alignment == LEADING) {
1597:                        x = x1;
1598:                        angle = 180;
1599:                    } else {
1600:                        x = x2;
1601:                        angle = 0;
1602:                    }
1603:                    x2--;
1604:                    int diam = Math.min(4, x2 - x1);
1605:                    Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
1606:                            BasicStroke.JOIN_BEVEL, 0, new float[] { 1, 1 }, 0);
1607:                    Stroke oldStroke = g.getStroke();
1608:                    g.setStroke(stroke);
1609:                    if (dimension == HORIZONTAL) {
1610:                        g.drawLine(x1, y, x2, y);
1611:                        angle += 90;
1612:                    } else {
1613:                        g.drawLine(y, x1, y, x2);
1614:                        int temp = x;
1615:                        x = y;
1616:                        y = temp;
1617:                    }
1618:                    g.setStroke(oldStroke);
1619:                    if ((alignment == LEADING) || (alignment == TRAILING)) {
1620:                        Object hint = g
1621:                                .getRenderingHint(RenderingHints.KEY_ANTIALIASING);
1622:                        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1623:                                RenderingHints.VALUE_ANTIALIAS_ON);
1624:                        g.fillArc(x - diam, y - diam, 2 * diam, 2 * diam,
1625:                                angle, 180);
1626:                        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1627:                                hint);
1628:                    }
1629:                }
1630:            }
1631:
1632:            private int[] visualIntervalPosition(LayoutInterval interval,
1633:                    int dimension, int alignment) {
1634:                int min = Short.MAX_VALUE;
1635:                int max = Short.MIN_VALUE;
1636:                if (interval.isParallel()
1637:                        && (interval.getGroupAlignment() != BASELINE)) {
1638:                    Iterator iter = interval.getSubIntervals();
1639:                    while (iter.hasNext()) {
1640:                        LayoutInterval subInterval = (LayoutInterval) iter
1641:                                .next();
1642:                        int imin, imax;
1643:                        int oppDim = (dimension == HORIZONTAL) ? VERTICAL
1644:                                : HORIZONTAL;
1645:                        if (LayoutInterval.isPlacedAtBorder(subInterval,
1646:                                oppDim, alignment)) {
1647:                            if (subInterval.isParallel()) {
1648:                                int[] ipos = visualIntervalPosition(
1649:                                        subInterval, dimension, alignment);
1650:                                imin = ipos[0];
1651:                                imax = ipos[1];
1652:                            } else if (!subInterval.isEmptySpace()) {
1653:                                LayoutRegion region = subInterval
1654:                                        .getCurrentSpace();
1655:                                imin = region.positions[dimension][LEADING];
1656:                                imax = region.positions[dimension][TRAILING];
1657:                            } else {
1658:                                imin = min;
1659:                                imax = max;
1660:                            }
1661:                        } else {
1662:                            imin = min;
1663:                            imax = max;
1664:                        }
1665:                        if (min > imin)
1666:                            min = imin;
1667:                        if (max < imax)
1668:                            max = imax;
1669:                    }
1670:                }
1671:                if (!interval.isParallel() || (min == Short.MAX_VALUE)) {
1672:                    LayoutRegion region = interval.getCurrentSpace();
1673:                    min = region.positions[dimension][LEADING];
1674:                    max = region.positions[dimension][TRAILING];
1675:                }
1676:                return new int[] { min, max };
1677:            }
1678:
1679:            // -----
1680:            // copying
1681:
1682:            /**
1683:             * Copy components with layout specified by sourceToTargetId map to
1684:             * the given target container. Assuming all components come from one
1685:             * container and one layer (layout roots pair), i.e. forming one piece of
1686:             * layout.
1687:             * If copying all components of one container to an empty container then
1688:             * exact 1:1 copy is created, otherwise the layout copy is placed into a
1689:             * separate layer not to interact with the existing layout.
1690:             * If copying within the same container, the copied components are placed
1691:             * slightly shifted from the original ones. Otherwise placed in the center.
1692:             * This method requires Ids for the copied components provided
1693:             * (LayoutComponent instances are cretated automatically).
1694:             * @param sourceModel the source LayoutModel
1695:             * @param sourceToTargetId components mapping between the original and the copied
1696:             *        components' Ids; same Ids can be used - then the components are
1697:             *        moved
1698:             * @param targetContainerId the Id of the target container
1699:             */
1700:            public void copyLayout(LayoutModel sourceModel,
1701:                    Map<String, String> sourceToTargetId,
1702:                    String targetContainerId) {
1703:                if (sourceToTargetId.size() == 0) {
1704:                    return;
1705:                }
1706:                if (sourceModel == null) {
1707:                    sourceModel = layoutModel;
1708:                }
1709:
1710:                Map.Entry<String, String> firstEntry = sourceToTargetId
1711:                        .entrySet().iterator().next();
1712:                LayoutComponent sourceContainer = sourceModel
1713:                        .getLayoutComponent(firstEntry.getKey()).getParent();
1714:                LayoutComponent targetContainer = layoutModel
1715:                        .getLayoutComponent(targetContainerId);
1716:                if (sourceContainer != targetContainer
1717:                        && sourceContainer.getSubComponentCount() == sourceToTargetId
1718:                                .size()
1719:                        && (targetContainer == null || targetContainer
1720:                                .getSubComponentCount() == 0)) {
1721:                    // copying/moving entire content of a container into an empty target container
1722:                    if (sourceModel != layoutModel
1723:                            || !firstEntry.getKey().equals(
1724:                                    firstEntry.getValue())) {
1725:                        layoutModel.copyContainerLayout(sourceContainer,
1726:                                sourceToTargetId, targetContainer);
1727:                    } else { // same source and target component - don't copy, just move
1728:                        layoutModel.moveContainerLayout(sourceContainer,
1729:                                targetContainer);
1730:                    }
1731:                } else { // copying part of the layout
1732:                    // collect the components, create new if needed, compute bounds, ...
1733:                    Map<LayoutComponent, LayoutComponent> sourceToTargetComp = new HashMap<LayoutComponent, LayoutComponent>();
1734:                    LayoutComponent[] sourceComponents = new LayoutComponent[sourceToTargetId
1735:                            .size()];
1736:                    LayoutComponent[] targetComponents = new LayoutComponent[sourceToTargetId
1737:                            .size()];
1738:                    Rectangle[] bounds = new Rectangle[sourceToTargetId.size()];
1739:                    LayoutRegion overallSpace = new LayoutRegion();
1740:                    LayoutInterval[] commonParents = new LayoutInterval[DIM_COUNT];
1741:                    int i = 0;
1742:                    for (Map.Entry<String, String> entry : sourceToTargetId
1743:                            .entrySet()) {
1744:                        String sourceId = entry.getKey();
1745:                        LayoutComponent sourceLC = sourceModel
1746:                                .getLayoutComponent(sourceId);
1747:                        String targetId = entry.getValue();
1748:                        LayoutComponent targetLC = layoutModel
1749:                                .getLayoutComponent(targetId);
1750:                        if (targetLC == null) {
1751:                            targetLC = new LayoutComponent(targetId, false);
1752:                        } else if (targetLC.getParent() == targetContainer) {
1753:                            throw new IllegalArgumentException(
1754:                                    "The component is already placed in the target layout container"); // NOI18N
1755:                        }
1756:                        sourceToTargetComp.put(sourceLC, targetLC);
1757:                        targetComponents[i] = targetLC;
1758:
1759:                        LayoutRegion space = sourceLC.getLayoutInterval(0)
1760:                                .getCurrentSpace();
1761:                        overallSpace.expand(space);
1762:                        bounds[i] = space.toRectangle(new Rectangle());
1763:                        sourceComponents[i] = sourceLC;
1764:                        i++;
1765:
1766:                        for (int dim = 0; dim < DIM_COUNT; dim++) {
1767:                            if (commonParents[dim] == null) {
1768:                                commonParents[dim] = sourceLC
1769:                                        .getLayoutInterval(dim);
1770:                            } else {
1771:                                commonParents[dim] = LayoutInterval
1772:                                        .getCommonParent(commonParents[dim],
1773:                                                sourceLC.getLayoutInterval(dim));
1774:                            }
1775:                        }
1776:                    }
1777:
1778:                    // copy the intervals
1779:                    LayoutInterval[] addingInts = new LayoutInterval[DIM_COUNT];
1780:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
1781:                        addingInts[dim] = restrictedCopy(commonParents[dim],
1782:                                sourceToTargetComp, overallSpace, dim, null);
1783:                    }
1784:                    // in case components are moved (not copied) make sure both the
1785:                    // components and intervals are correctly removed from the original place
1786:                    if (targetComponents.length > 1) {
1787:                        // for multiple components the intervals are extracted in restrictedCopy
1788:                        // (their common parent is removed), so now also remove the components
1789:                        // so addComponents does not try to remove the intervals later
1790:                        for (LayoutComponent comp : targetComponents) {
1791:                            if (comp.getParent() != null) {
1792:                                layoutModel.removeComponent(comp, false);
1793:                            }
1794:                        }
1795:                    } // for single component both the component and intervals will be removed in addComponents
1796:
1797:                    // place the copied intervals
1798:                    int[] shift = getCopyShift(sourceComponents,
1799:                            targetContainer, overallSpace,
1800:                            sourceContainer == targetContainer);
1801:                    if (shift != null) {
1802:                        prepareDragger(targetComponents, bounds,
1803:                                new Point(0, 0), LayoutDragger.ALL_EDGES);
1804:                        dragger.setTargetContainer(targetContainer,
1805:                                getTargetRootsForCopy(targetContainer));
1806:                        dragger.move(shift, false, false);
1807:                        addComponents(targetComponents, targetContainer,
1808:                                addingInts, false);
1809:                        dragger = null;
1810:                    } else {
1811:                        addUnspecified(targetComponents, targetContainer,
1812:                                addingInts);
1813:                    }
1814:                }
1815:                visualStateUpToDate = false;
1816:            }
1817:
1818:            /**
1819:             * Copy components without layout to the given target container. The layout
1820:             * is determined from the visual bounds of the components. Can be used for
1821:             * copying external components (from different layout) to a container, or
1822:             * for converting container's layout to this layout model. New
1823:             * LayoutComponent instances are created as needed, added all to the
1824:             * specified target container (centered, in separate layer of layout roots).
1825:             * @param idToBounds mapping component Ids to the visual bounds
1826:             * @param targetContainerId the Id of the target container
1827:             * @param relative if true then the position of the entire formation
1828:             *        is set to 0, 0 before the conversion starts
1829:             */
1830:            public void copyLayoutFromOutside(
1831:                    Map<String, Rectangle> idToBounds,
1832:                    String targetContainerId, boolean relative) {
1833:                LayoutComponent targetContainer = layoutModel
1834:                        .getLayoutComponent(targetContainerId);
1835:                if (targetContainer.getSubComponentCount() > 0) {
1836:                    relative = true;
1837:                }
1838:                Map<LayoutComponent, Rectangle> compToBounds = new HashMap<LayoutComponent, Rectangle>();
1839:                LayoutComponent[] components = new LayoutComponent[idToBounds
1840:                        .size()];
1841:                int minX = Integer.MAX_VALUE;
1842:                int minY = Integer.MAX_VALUE;
1843:                int i = 0;
1844:                for (Map.Entry<String, Rectangle> entry : idToBounds.entrySet()) {
1845:                    String targetId = entry.getKey();
1846:                    LayoutComponent targetLC = layoutModel
1847:                            .getLayoutComponent(targetId);
1848:                    if (targetLC == null) {
1849:                        targetLC = new LayoutComponent(targetId, false);
1850:                    } else if (targetLC.getParent() != null) {
1851:                        throw new IllegalArgumentException(
1852:                                "Target component already exists and is placed in the layout"); // NOI18N
1853:                    }
1854:                    Rectangle r = new Rectangle(entry.getValue());
1855:                    compToBounds.put(targetLC, r);
1856:                    components[i] = targetLC;
1857:                    LayoutRegion compSpace = new LayoutRegion(r,
1858:                            LayoutRegion.UNKNOWN);
1859:                    Dimension preferred = visualMapper
1860:                            .getComponentPreferredSize(targetId);
1861:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
1862:                        int size = compSpace.size(dim);
1863:                        if (preferred == null
1864:                                || size != ((dim == HORIZONTAL) ? preferred.width
1865:                                        : preferred.height)) {
1866:                            targetLC.getLayoutInterval(dim).setPreferredSize(
1867:                                    compSpace.size(dim));
1868:                        }
1869:                    }
1870:                    if (relative) {
1871:                        minX = Math.min(minX,
1872:                                compSpace.positions[HORIZONTAL][LEADING]);
1873:                        minY = Math.min(minY,
1874:                                compSpace.positions[VERTICAL][LEADING]);
1875:                    }
1876:                    i++;
1877:                }
1878:                if (relative) {
1879:                    // need the overall bounds enclosure based on 0, 0
1880:                    for (Rectangle r : compToBounds.values()) {
1881:                        r.x -= minX;
1882:                        r.y -= minY;
1883:                    }
1884:                }
1885:                LayoutInterval[] addingInts = LayoutModel
1886:                        .createIntervalsFromBounds(compToBounds);
1887:                if (relative) {
1888:                    addUnspecified(components, targetContainer, addingInts);
1889:                } else {
1890:                    addToEmpty(components, targetContainer, addingInts);
1891:                }
1892:                visualStateUpToDate = false;
1893:            }
1894:
1895:            /**
1896:             * Duplicates layout of given components. Duplicated components are added
1897:             * sequentially along given axis (dimension), in parallel in the orthogonal
1898:             * dimension.
1899:             * @param sourceIds Ids of the source components
1900:             * @param targetIds Ids for the duplicates (non-existing layout components
1901:             *        are created automatically with the given Ids)
1902:             * @param dimension the dimension in which the layout should be duplicated
1903:             *        (extended sequentially); HORIZONTAL, VERTICAL, or < 0
1904:             * @param direction the direction of addition - LEADING or TRAILING
1905:             */
1906:            public void duplicateLayout(String[] sourceIds, String[] targetIds,
1907:                    int dimension, int direction) {
1908:                LayoutComponent[] sourceComps = new LayoutComponent[sourceIds.length];
1909:                LayoutInterval[][] sourceIntervals = new LayoutInterval[DIM_COUNT][sourceIds.length];
1910:                LayoutComponent[] targetComps = new LayoutComponent[targetIds.length];
1911:                Map<LayoutComponent, LayoutComponent> compMap = new HashMap<LayoutComponent, LayoutComponent>();
1912:                LayoutComponent container = null;
1913:                for (int i = 0; i < sourceComps.length; i++) {
1914:                    LayoutComponent sourceLC = layoutModel
1915:                            .getLayoutComponent(sourceIds[i]);
1916:                    LayoutComponent parent = sourceLC.getParent();
1917:                    if (i == 0) {
1918:                        container = parent;
1919:                    } else if (parent != container) {
1920:                        throw new IllegalArgumentException(
1921:                                "Duplicated components must be in the same container."); // NOI18N
1922:                    }
1923:                    sourceComps[i] = sourceLC;
1924:
1925:                    LayoutComponent targetLC = layoutModel
1926:                            .getLayoutComponent(targetIds[i]);
1927:                    if (targetLC == null) {
1928:                        targetLC = new LayoutComponent(targetIds[i], false);
1929:                    } else if (targetLC.getParent() != null) {
1930:                        throw new IllegalArgumentException(
1931:                                "Target component already exists and is placed in the layout"); // NOI18N
1932:                    }
1933:                    layoutModel.addComponent(targetLC, container, -1);
1934:                    compMap.put(sourceLC, targetLC);
1935:                    targetComps[i] = targetLC;
1936:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
1937:                        LayoutInterval li = sourceLC.getLayoutInterval(dim);
1938:                        sourceIntervals[dim][i] = li;
1939:                    }
1940:                }
1941:
1942:                int seqDim = dimension < 0 ? getSeqDuplicatingDimension(sourceComps)
1943:                        : dimension;
1944:                int seqDir = direction < 0 ? getSeqDuplicatingDirection(
1945:                        sourceComps, seqDim) : direction;
1946:                int parDim = seqDim ^ 1;
1947:                try {
1948:                    modelListener.deactivate(); // do not react on model changes during adding
1949:
1950:                    duplicateSequentially(sourceIntervals[seqDim], compMap,
1951:                            seqDim, seqDir);
1952:                    duplicateInParallel(sourceIntervals[parDim], compMap,
1953:                            parDim);
1954:
1955:                    visualStateUpToDate = false;
1956:                    optimizeStructure = true;
1957:                    updateDesignModifications(container);
1958:                } finally {
1959:                    if (!modelListener.isActive()) {
1960:                        modelListener.activate();
1961:                    }
1962:                }
1963:            }
1964:
1965:            private static int getSeqDuplicatingDimension(
1966:                    LayoutComponent[] components) {
1967:                return VERTICAL;
1968:            }
1969:
1970:            private static int getSeqDuplicatingDirection(
1971:                    LayoutComponent[] components, int dimension) {
1972:                return TRAILING;
1973:            }
1974:
1975:            private void duplicateSequentially(LayoutInterval[] intervals,
1976:                    Map<LayoutComponent, LayoutComponent> componentMap,
1977:                    int dimension, int direction) {
1978:                // get the root groups/components to duplicate, prepare parent sequences
1979:                Set<LayoutInterval> dupRoots = new HashSet<LayoutInterval>();
1980:                Set<LayoutInterval> dupParents = new HashSet<LayoutInterval>();
1981:                for (LayoutInterval li : intervals) {
1982:                    LayoutInterval parent = li.getParent();
1983:                    while (parent != null) {
1984:                        if (dupRoots.contains(parent)) {
1985:                            break;
1986:                        } else {
1987:                            parent = parent.getParent();
1988:                        }
1989:                    }
1990:                    if (parent == null) { // interval not known yet
1991:                        parent = li.getParent();
1992:                        while (parent != null) {
1993:                            if (shouldDuplicateWholeGroup(parent, li, intervals)) {
1994:                                li = parent;
1995:                                parent = li.getParent();
1996:                            } else {
1997:                                dupRoots.add(li);
1998:                                LayoutInterval targetSeq;
1999:                                if (li.isSequential()) {
2000:                                    targetSeq = li;
2001:                                } else if (parent.isSequential()) {
2002:                                    targetSeq = parent;
2003:                                } else {
2004:                                    LayoutInterval seq = new LayoutInterval(
2005:                                            SEQUENTIAL);
2006:                                    layoutModel.addInterval(seq, parent,
2007:                                            layoutModel.removeInterval(li));
2008:                                    layoutModel.addInterval(li, seq, -1);
2009:                                    targetSeq = seq;
2010:                                }
2011:                                dupParents.add(targetSeq);
2012:                                break;
2013:                            }
2014:                        }
2015:                        if (parent == null) { // the root duplicated
2016:                            parent = li;
2017:                            LayoutInterval group = new LayoutInterval(PARALLEL);
2018:                            group.setGroupAlignment(parent.getGroupAlignment());
2019:                            while (parent.getSubIntervalCount() > 0) {
2020:                                layoutModel.addInterval(layoutModel
2021:                                        .removeInterval(parent, 0), group, -1);
2022:                            }
2023:                            LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
2024:                            layoutModel.addInterval(seq, parent, -1);
2025:                            layoutModel.addInterval(group, seq, -1);
2026:                            dupParents.add(seq);
2027:                        }
2028:                    }
2029:                }
2030:
2031:                // duplicate...
2032:                for (LayoutInterval seq : dupParents) {
2033:                    int start = -1;
2034:                    boolean wholeSeq = dupRoots.contains(seq);
2035:                    LayoutRegion space = seq.getParent().getCurrentSpace();
2036:                    for (int i = 0; i < seq.getSubIntervalCount(); i++) {
2037:                        LayoutInterval sub = seq.getSubInterval(i);
2038:                        boolean duplicate = !sub.isEmptySpace()
2039:                                && (wholeSeq || dupRoots.contains(sub));
2040:                        boolean last = (i + 1 == seq.getSubIntervalCount());
2041:                        if (duplicate && start < 0) {
2042:                            start = i;
2043:                        }
2044:                        if (start >= 0
2045:                                && ((!duplicate && !sub.isEmptySpace()) || last)) {
2046:                            int count = i - start;
2047:                            if (last) {
2048:                                if (!sub.isEmptySpace()) {
2049:                                    count++;
2050:                                }
2051:                            } else if (seq.getSubInterval(i - 1).isEmptySpace()) {
2052:                                count--;
2053:                            }
2054:                            if (count > 0) { // copy the continuous section within the sequence
2055:                                for (int j = start; j < start + count; j++) {
2056:                                    LayoutInterval li = seq.getSubInterval(j);
2057:                                    LayoutInterval copy = restrictedCopy(li,
2058:                                            componentMap, space, dimension,
2059:                                            null);
2060:                                    if (direction == LEADING) {
2061:                                        layoutModel.addInterval(copy, seq,
2062:                                                start);
2063:                                        start++;
2064:                                        i++;
2065:                                        j++;
2066:                                    } else { // TRAILING
2067:                                        layoutModel.addInterval(copy, seq, j
2068:                                                + count);
2069:                                        i++;
2070:                                    }
2071:                                }
2072:                                // need a gap between the original and duplicated section
2073:                                int gapIndex;
2074:                                LayoutInterval gap = null;
2075:                                if (direction == LEADING) {
2076:                                    gapIndex = start + count; // here should be the original gap to use
2077:                                    if (gapIndex < seq.getSubIntervalCount()) {
2078:                                        gap = seq.getSubInterval(gapIndex);
2079:                                    }
2080:                                    gapIndex = start; // here it should be placed
2081:                                } else { // TRAILING
2082:                                    gapIndex = start - 1; // here should be the original gap to use
2083:                                    if (gapIndex >= 0) {
2084:                                        gap = seq.getSubInterval(gapIndex);
2085:                                    }
2086:                                    gapIndex = start + count; // here it should be placed
2087:                                }
2088:                                LayoutInterval newGap = new LayoutInterval(
2089:                                        SINGLE);
2090:                                if (gap != null && gap.isEmptySpace()) {
2091:                                    LayoutInterval.cloneInterval(gap, newGap);
2092:                                }
2093:                                layoutModel.addInterval(newGap, seq, gapIndex);
2094:                                i++;
2095:                            }
2096:                            start = -1;
2097:                        }
2098:                    }
2099:                }
2100:            }
2101:
2102:            private static boolean shouldDuplicateWholeGroup(
2103:                    LayoutInterval group, LayoutInterval knownSub,
2104:                    LayoutInterval[] dupIntervals) {
2105:                assert group.isGroup();
2106:                Iterator it = group.getSubIntervals();
2107:                while (it.hasNext()) {
2108:                    LayoutInterval sub = (LayoutInterval) it.next();
2109:                    if (sub == knownSub || sub.isEmptySpace()) {
2110:                        continue;
2111:                    }
2112:                    boolean included;
2113:                    if (sub.isGroup()) {
2114:                        included = shouldDuplicateWholeGroup(sub, null,
2115:                                dupIntervals);
2116:                    } else {
2117:                        assert sub.isComponent();
2118:                        included = false;
2119:                        for (LayoutInterval li : dupIntervals) {
2120:                            if (li == sub) {
2121:                                included = true;
2122:                                break;
2123:                            }
2124:                        }
2125:                    }
2126:                    if (included && group.isParallel()) {
2127:                        return true; // one is enough in parallel group
2128:                    }
2129:                    if (!included && group.isSequential()) {
2130:                        return false; // all required in a sequence
2131:                    }
2132:                }
2133:                return group.isSequential();
2134:            }
2135:
2136:            private void duplicateInParallel(LayoutInterval[] intervals,
2137:                    Map<LayoutComponent, LayoutComponent> componentMap,
2138:                    int dimension) {
2139:                Map<LayoutInterval, LayoutInterval> intMap = new HashMap<LayoutInterval, LayoutInterval>();
2140:                for (LayoutInterval li : intervals) {
2141:                    intMap.put(li, componentMap.get(li.getComponent())
2142:                            .getLayoutInterval(dimension));
2143:                }
2144:                for (LayoutInterval li : intervals) {
2145:                    LayoutInterval copy = intMap.get(li);
2146:                    if (copy == null) {
2147:                        continue;
2148:                    }
2149:                    LayoutInterval parent = li.getParent();
2150:                    if (parent.isParallel()) {
2151:                        LayoutInterval.cloneInterval(li, copy);
2152:                        layoutModel.setIntervalAlignment(copy, li
2153:                                .getRawAlignment());
2154:                        layoutModel.addInterval(copy, parent, -1);
2155:                    } else { // in sequence
2156:                        int index = parent.indexOf(li);
2157:                        int start = getDuplicationBoundary(parent, index,
2158:                                intMap.keySet(), LEADING);
2159:                        int end = getDuplicationBoundary(parent, index, intMap
2160:                                .keySet(), TRAILING);
2161:                        int gapStart = LayoutUtils.getVisualPosition(parent
2162:                                .getSubInterval(start), dimension, LEADING);
2163:                        LayoutInterval normalGap = null;
2164:                        boolean substGap = false;
2165:                        LayoutInterval parSeq = new LayoutInterval(SEQUENTIAL);
2166:                        for (int i = start; i <= end; i++) {
2167:                            LayoutInterval sub = parent.getSubInterval(i);
2168:                            copy = intMap.remove(sub);
2169:                            if (copy != null) {
2170:                                LayoutInterval.cloneInterval(sub, copy);
2171:                                if (normalGap != null) {
2172:                                    LayoutInterval copyGap = new LayoutInterval(
2173:                                            SINGLE);
2174:                                    LayoutInterval.cloneInterval(normalGap,
2175:                                            copyGap);
2176:                                    layoutModel
2177:                                            .addInterval(copyGap, parSeq, -1);
2178:                                    normalGap = null;
2179:                                } else if (substGap) {
2180:                                    LayoutInterval gap = new LayoutInterval(
2181:                                            SINGLE);
2182:                                    int gapEnd = sub.getCurrentSpace().positions[dimension][LEADING];
2183:                                    gap.setSize(gapEnd - gapStart);
2184:                                    layoutModel.addInterval(gap, parSeq, -1);
2185:                                    substGap = false;
2186:                                }
2187:                                layoutModel.addInterval(copy, parSeq, -1);
2188:                                gapStart = sub.getCurrentSpace().positions[dimension][TRAILING];
2189:                            } else if (!sub.isEmptySpace()) { // skipped component
2190:                                normalGap = null;
2191:                                substGap = true;
2192:                            } else if (!substGap) { // normal gap
2193:                                normalGap = sub;
2194:                            }
2195:                        }
2196:                        if (normalGap != null) {
2197:                            LayoutInterval copyGap = new LayoutInterval(SINGLE);
2198:                            LayoutInterval.cloneInterval(normalGap, copyGap);
2199:                            layoutModel.addInterval(copyGap, parSeq, -1);
2200:                        } else if (substGap) {
2201:                            LayoutInterval gap = new LayoutInterval(SINGLE);
2202:                            int gapEnd = LayoutUtils.getVisualPosition(parent
2203:                                    .getSubInterval(end), dimension, TRAILING);
2204:                            gap.setSize(gapEnd - gapStart);
2205:                            layoutModel.addInterval(gap, parSeq, -1);
2206:                        }
2207:                        operations.addParallelWithSequence(parSeq, parent,
2208:                                start, end, dimension);
2209:                    }
2210:                }
2211:            }
2212:
2213:            private static int getDuplicationBoundary(LayoutInterval seq,
2214:                    int index, Set<LayoutInterval> dupIntervals, int direction) {
2215:                assert seq.isSequential();
2216:                int d = (direction == LEADING ? -1 : 1);
2217:                index += d;
2218:                while (index >= 0 && index < seq.getSubIntervalCount()) {
2219:                    LayoutInterval sub = seq.getSubInterval(index);
2220:                    if (sub.isParallel()) {
2221:                        break; // [This forces enclosing the component in a closed group
2222:                        // which might be unnecessary (at least in horizontal dimension, in
2223:                        // vertical it is most probably ok). Maybe we should parallelize with
2224:                        // parallel members if they don't contain another duplicated component.]
2225:                    }
2226:                    index += d;
2227:                }
2228:                return index - d;
2229:            }
2230:
2231:            public void encloseInContainer(String[] compIds, String contId) {
2232:                LayoutComponent enclosingCont = layoutModel
2233:                        .getLayoutComponent(contId);
2234:                if (enclosingCont == null) {
2235:                    enclosingCont = new LayoutComponent(contId, true);
2236:                } else if (enclosingCont.getParent() != null) {
2237:                    throw new IllegalArgumentException(
2238:                            "Target container already exists and is placed in the layout"); // NOI18N
2239:                } else if (enclosingCont.getSubComponentCount() > 0) {
2240:                    throw new IllegalArgumentException(
2241:                            "Target container is not empty."); // NOI18N
2242:                }
2243:                LayoutComponent parentCont = null;
2244:                LayoutComponent[] components = new LayoutComponent[compIds.length];
2245:                //        LayoutComponent[][] borderComps = new LayoutComponent[DIM_COUNT][2];
2246:                LayoutInterval[] commonParents = new LayoutInterval[DIM_COUNT];
2247:                boolean[] resizing = new boolean[DIM_COUNT];
2248:                Map<LayoutComponent, LayoutComponent> compMap = new HashMap<LayoutComponent, LayoutComponent>();
2249:                LayoutRegion overallSpace = new LayoutRegion();
2250:                int i = 0;
2251:                for (String id : compIds) {
2252:                    LayoutComponent comp = layoutModel.getLayoutComponent(id);
2253:                    components[i++] = comp;
2254:                    compMap.put(comp, comp);
2255:
2256:                    if (parentCont == null) {
2257:                        parentCont = comp.getParent();
2258:                    }
2259:                    LayoutRegion space = comp.getLayoutInterval(0)
2260:                            .getCurrentSpace();
2261:
2262:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2263:                        //                if (!overallSpace.isSet(dim) || space.positions[dim][LEADING] < overallSpace.positions[dim][LEADING]) {
2264:                        //                    borderComps[dim][LEADING] = comp;
2265:                        //                } else if (!overallSpace.isSet(dim) || space.positions[dim][TRAILING] > overallSpace.positions[dim][TRAILING]) {
2266:                        //                    borderComps[dim][TRAILING] = comp;
2267:                        //                }
2268:                        overallSpace.expand(space, dim);
2269:
2270:                        if (commonParents[dim] == null) {
2271:                            commonParents[dim] = comp.getLayoutInterval(dim);
2272:                        } else {
2273:                            commonParents[dim] = LayoutInterval
2274:                                    .getCommonParent(commonParents[dim], comp
2275:                                            .getLayoutInterval(dim));
2276:                        }
2277:                    }
2278:                }
2279:                LayoutInterval[] parentRoots = new LayoutInterval[DIM_COUNT];
2280:                for (int dim = 0; dim < DIM_COUNT; dim++) {
2281:                    parentRoots[dim] = LayoutInterval.getRoot(components[0]
2282:                            .getLayoutInterval(dim));
2283:                    resizing[dim] = LayoutInterval
2284:                            .wantResize(commonParents[dim]);
2285:                }
2286:                if (enclosingCont.isLayoutContainer()) {
2287:                    LayoutInterval[] extractedInts = new LayoutInterval[DIM_COUNT];
2288:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2289:                        LayoutInterval extract = commonParents[dim];
2290:                        if (extract.isComponent()) { // just one component being enclosed
2291:                            layoutModel.removeInterval(extract);
2292:                            extractedInts[dim] = extract;
2293:                        } else {
2294:                            extractedInts[dim] = restrictedCopy(extract,
2295:                                    compMap, overallSpace, dim, null);
2296:                        }
2297:                    }
2298:                    for (LayoutComponent comp : components) {
2299:                        layoutModel.removeComponent(comp, false);
2300:                        layoutModel.addComponent(comp, enclosingCont, -1);
2301:                    }
2302:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2303:                        // make sure the root is empty (clear possible "offset" gap) 
2304:                        LayoutInterval root = enclosingCont
2305:                                .getDefaultLayoutRoot(dim);
2306:                        for (int n = root.getSubIntervalCount(); n > 0; n--) {
2307:                            root.remove(n - 1);
2308:                        }
2309:                        assert root.isParallel();
2310:                        LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
2311:                        seq.add(new LayoutInterval(SINGLE), -1);
2312:                        layoutModel.addInterval(extractedInts[dim], seq, -1);
2313:                        seq.add(new LayoutInterval(SINGLE), -1);
2314:                        layoutModel.addInterval(seq, root, -1);
2315:                    }
2316:                } else {
2317:                    for (LayoutComponent comp : components) {
2318:                        layoutModel.removeComponentAndIntervals(comp, !comp
2319:                                .isLayoutContainer());
2320:                    }
2321:                }
2322:                LayoutInterval[] addingInts = new LayoutInterval[DIM_COUNT];
2323:                for (int dim = 0; dim < DIM_COUNT; dim++) {
2324:                    LayoutInterval interval = enclosingCont
2325:                            .getLayoutInterval(dim);
2326:                    addingInts[dim] = interval;
2327:                    interval.setSizes(USE_PREFERRED_SIZE, DEFAULT,
2328:                            resizing[dim] ? Short.MAX_VALUE
2329:                                    : USE_PREFERRED_SIZE);
2330:                }
2331:                // provisionally use dragger to position the container; maybe could be done better
2332:                prepareDragger(new LayoutComponent[] { enclosingCont },
2333:                        new Rectangle[] { overallSpace
2334:                                .toRectangle(new Rectangle()) },
2335:                        new Point(0, 0), LayoutDragger.ALL_EDGES);
2336:                dragger.setTargetContainer(parentCont, parentRoots);
2337:                dragger.move(new int[] { 10, 10 }, true, false);
2338:                dragger.move(new int[] { 0, 0 }, true, false);
2339:                addComponents(new LayoutComponent[] { enclosingCont },
2340:                        parentCont, addingInts, false);
2341:                dragger = null;
2342:                visualStateUpToDate = false;
2343:            }
2344:
2345:            /**
2346:             * Add a single new component (targetId) to given target container. No layout
2347:             * information is provided, so the component is placed on a default location
2348:             * in the layout. This means centered within the container, in a separate
2349:             * layer of layout roots (not to interact with the rest of the layout).
2350:             * The size of the component can be determined either by a "source"
2351:             * component (typically if adding a copy of another component), or by
2352:             * provided Dimension object (compSize).
2353:             * @param targetId Id of the added component; may already be in the model,
2354:             *        but not in a container; created automatically if needed
2355:             * @param sourceId Id of the source component - optional - if provided, the
2356:             *        size definition of this component is copied to the added component
2357:             * @param compSize the size of the component - used if the source component
2358:             *        is not provided to determine the size from; can be null
2359:             * @param targetContainerId the Id of the target container (must exist in the model)
2360:             */
2361:            public void addUnspecifiedComponent(String targetId,
2362:                    String sourceId, Dimension compSize,
2363:                    String targetContainerId) {
2364:                LayoutComponent targetContainer = layoutModel
2365:                        .getLayoutComponent(targetContainerId);
2366:                LayoutComponent targetLC = layoutModel
2367:                        .getLayoutComponent(targetId);
2368:                if (targetLC == null) {
2369:                    targetLC = new LayoutComponent(targetId, false);
2370:                } else if (targetLC.getParent() != null) {
2371:                    throw new IllegalArgumentException(
2372:                            "Target component already exists and is placed in the layout"); // NOI18N
2373:                }
2374:                LayoutComponent sourceLC = layoutModel
2375:                        .getLayoutComponent(sourceId);
2376:                LayoutInterval[] addingInts = new LayoutInterval[DIM_COUNT];
2377:                if (sourceLC != null) {
2378:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2379:                        LayoutInterval li = sourceLC.getLayoutInterval(dim);
2380:                        addingInts[dim] = LayoutInterval.cloneInterval(li,
2381:                                targetLC.getLayoutInterval(dim));
2382:                    }
2383:                } else {
2384:                    Dimension preferred = compSize != null ? visualMapper
2385:                            .getComponentPreferredSize(targetId) : null;
2386:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2387:                        LayoutInterval li = targetLC.getLayoutInterval(dim);
2388:                        addingInts[dim] = li;
2389:                        if (preferred != null) {
2390:                            int size = (dim == HORIZONTAL) ? compSize.width
2391:                                    : compSize.height;
2392:                            int pref = (dim == HORIZONTAL) ? preferred.width
2393:                                    : preferred.height;
2394:                            if (size != pref) {
2395:                                li.setPreferredSize(size);
2396:                            }
2397:                        }
2398:                    }
2399:                }
2400:                addUnspecified(new LayoutComponent[] { targetLC },
2401:                        targetContainer, /*getTargetRootsForCopy(targetContainer),*/
2402:                        addingInts);
2403:                visualStateUpToDate = false;
2404:            }
2405:
2406:            /**
2407:             * Add a new component to the target layout. No layout information is
2408:             * provided, so the component is added to a default location. (This means
2409:             * centered within the container, in a separate layer of layout roots - not
2410:             * to interact with the rest of the layout.)
2411:             * @param component the component to be added; may already exist in the model,
2412:             *        but not placed in a container
2413:             * @param targetContainerId Id of the target container; can be null
2414:             */
2415:            public void addUnspecifiedComponent(LayoutComponent component,
2416:                    String targetContainerId) {
2417:                if (component.getParent() != null) {
2418:                    throw new IllegalArgumentException(
2419:                            "The component already exists and is placed in the layout"); // NOI18N
2420:                }
2421:                LayoutComponent targetContainer = layoutModel
2422:                        .getLayoutComponent(targetContainerId);
2423:                if (targetContainer == null) {
2424:                    layoutModel.addRootComponent(component);
2425:                } else {
2426:                    LayoutInterval[] addingInts = new LayoutInterval[DIM_COUNT];
2427:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2428:                        addingInts[dim] = component.getLayoutInterval(dim);
2429:                    }
2430:                    addUnspecified(new LayoutComponent[] { component },
2431:                            targetContainer, addingInts);
2432:                    visualStateUpToDate = false;
2433:                }
2434:            }
2435:
2436:            private LayoutInterval[] getTargetRootsForCopy(
2437:                    LayoutComponent targetContainer) {
2438:                LayoutInterval[] roots = layoutModel
2439:                        .addNewLayoutRoots(targetContainer);
2440:                LayoutRegion space = getContainerSpace(targetContainer);
2441:                for (int i = 0; i < DIM_COUNT; i++) {
2442:                    roots[i].setCurrentSpace(space);
2443:                }
2444:                return roots;
2445:            }
2446:
2447:            private static int[] getCopyShift(
2448:                    LayoutComponent[] sourceComponents,
2449:                    LayoutComponent targetContainer, LayoutRegion compSpace,
2450:                    boolean relative) {
2451:                LayoutRegion contSpace = getContainerSpace(targetContainer);
2452:                if (!compSpace.isSet() || !contSpace.isSet()) {
2453:                    return null;
2454:                }
2455:                int[] move = new int[DIM_COUNT];
2456:                if (relative) {
2457:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2458:                        move[dim] = suggestCopyShift(sourceComponents, dim);
2459:                    }
2460:                    if (move[HORIZONTAL] == 0 && move[VERTICAL] == 0) {
2461:                        move[HORIZONTAL] = move[VERTICAL] = 10;
2462:                    }
2463:                } else {
2464:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
2465:                        move[dim] = (contSpace.size(dim) - compSpace.size(dim))
2466:                                / 2 - compSpace.positions[dim][LEADING]
2467:                                + contSpace.positions[dim][LEADING];
2468:                    }
2469:
2470:                }
2471:                return move;
2472:            }
2473:
2474:            private static int suggestCopyShift(
2475:                    LayoutComponent[] sourceComponents, int dimension) {
2476:                if (!isAnyComponentSnappedToRoot(sourceComponents, dimension,
2477:                        TRAILING)) {
2478:                    return 10;
2479:                } else if (!isAnyComponentSnappedToRoot(sourceComponents,
2480:                        dimension, LEADING)) {
2481:                    return -10;
2482:                } else {
2483:                    return 0; // snapped to root border on both sides
2484:                }
2485:            }
2486:
2487:            private static boolean isAnyComponentSnappedToRoot(
2488:                    LayoutComponent[] components, int dimension, int alignment) {
2489:                LayoutInterval root = null;
2490:                for (LayoutComponent comp : components) {
2491:                    LayoutInterval compInt = comp.getLayoutInterval(dimension);
2492:                    if (root == null) {
2493:                        root = LayoutInterval.getRoot(compInt);
2494:                    }
2495:                    if (LayoutInterval.isPlacedAtBorder(compInt, root,
2496:                            dimension, alignment)
2497:                            || isSnappedNextToInParent(compInt, root,
2498:                                    dimension, alignment)) {
2499:                        return true;
2500:                    }
2501:                }
2502:                return false;
2503:            }
2504:
2505:            // [move this to LayoutInterval?]
2506:            private static boolean isSnappedNextToInParent(
2507:                    LayoutInterval interval, LayoutInterval parent,
2508:                    int dimension, int alignment) {
2509:                LayoutInterval gap = LayoutInterval.getNeighbor(interval,
2510:                        alignment, false, true, false);
2511:                if (gap != null && LayoutInterval.isFixedDefaultPadding(gap)
2512:                        && parent.isParentOf(gap)) {
2513:                    LayoutInterval back = LayoutInterval.getDirectNeighbor(gap,
2514:                            alignment ^ 1, true);
2515:                    if ((back == interval || LayoutInterval.isPlacedAtBorder(
2516:                            interval, back, dimension, alignment))
2517:                            && LayoutInterval.getNeighbor(gap, alignment, true,
2518:                                    true, false) == null
2519:                            && LayoutInterval.isPlacedAtBorder(gap.getParent(),
2520:                                    parent, dimension, alignment)) {
2521:                        return true;
2522:                    }
2523:                }
2524:                return false;
2525:            }
2526:
2527:            private static LayoutRegion getContainerSpace(
2528:                    LayoutComponent container) {
2529:                return container.getDefaultLayoutRoot(HORIZONTAL)
2530:                        .getCurrentSpace();
2531:            }
2532:
2533:            private LayoutInterval[] getActiveLayoutRoots(
2534:                    LayoutComponent container) {
2535:                return container.getLayoutRoots().get(0);
2536:                // [in future we may keep the default "layer" for each container stored somewhere]
2537:            }
2538:
2539:            // -----
2540:            // LayoutModel.Listener implementation & related
2541:
2542:            class Listener implements  LayoutModel.Listener {
2543:                private boolean active = false;
2544:
2545:                public void layoutChanged(LayoutEvent ev) {
2546:                    if (!layoutModel.isUndoRedoInProgress()) {
2547:                        deactivate();
2548:                        LayoutDesigner.this .layoutChanged(ev);
2549:                        activate();
2550:                    }
2551:                }
2552:
2553:                void activate() {
2554:                    layoutModel.addListener(this );
2555:                    active = true;
2556:                }
2557:
2558:                void deactivate() {
2559:                    layoutModel.removeListener(this );
2560:                    active = false;
2561:                }
2562:
2563:                boolean isActive() {
2564:                    return active;
2565:                }
2566:            }
2567:
2568:            private void layoutChanged(LayoutEvent e) {
2569:                if (e.getType() == LayoutEvent.INTERVAL_REMOVED) {
2570:                    // component interval was removed - need to clear neighbor gaps etc.
2571:                    LayoutEvent.Interval ev = (LayoutEvent.Interval) e;
2572:                    LayoutInterval interval = ev.getInterval();
2573:                    LayoutComponent comp = interval.getComponent();
2574:                    if (comp != null) {
2575:                        int dim = -1;
2576:                        for (int i = 0; i < DIM_COUNT; i++) {
2577:                            if (comp.getLayoutInterval(i) == interval) {
2578:                                dim = i;
2579:                                break;
2580:                            }
2581:                        }
2582:                        assert dim > -1;
2583:                        LayoutInterval root = LayoutInterval.getRoot(ev
2584:                                .getParentInterval());
2585:                        intervalRemoved(ev.getParentInterval(), ev.getIndex(),
2586:                                true, LayoutInterval.wantResize(interval), dim);
2587:                        LayoutComponent container = comp.getParent();
2588:                        if (container != null
2589:                                && root.getSubIntervalCount() == 0) {
2590:                            // Empty root - eliminate if it is an additional layer or
2591:                            // default layer with just one additional (which then
2592:                            // becomes default).
2593:                            // Hack #127988: don't remove layout roots during resizing
2594:                            // (resized component stays in additional layer - unlike moved).
2595:                            boolean resizing = (dragger != null && dragger
2596:                                    .isResizing());
2597:                            if (root == getActiveLayoutRoots(container)[dim]
2598:                                    && (container.getLayoutRootCount() != 2 || resizing)) {
2599:                                propEmptyContainer(root, dim);
2600:                            } else if (!resizing) {
2601:                                layoutModel.removeLayoutRoots(container, root);
2602:                            }
2603:                        }
2604:                    }
2605:                }
2606:            }
2607:
2608:            // -----
2609:
2610:            private boolean isComponentResizable(LayoutComponent comp,
2611:                    int dimension) {
2612:                boolean[] res = comp.getResizability();
2613:                if (res == null) {
2614:                    res = visualMapper.getComponentResizability(comp.getId(),
2615:                            new boolean[DIM_COUNT]);
2616:                    comp.setResizability(res);
2617:                }
2618:                return res[dimension];
2619:            }
2620:
2621:            /**
2622:             * Changes global alignment of the layout component.
2623:             *
2624:             * @param comp component whose alignment should be changed.
2625:             * @param dimension dimension the alignment should be applied in.
2626:             * @param alignment desired alignment.
2627:             */
2628:            public void adjustComponentAlignment(LayoutComponent comp,
2629:                    int dimension, int alignment) {
2630:                if (logTestCode()) {
2631:                    testCode.add("// > ADJUST COMPONENT ALIGNMENT"); //NOI18N
2632:                    testCode.add("{"); //NOI18N
2633:                    testCode
2634:                            .add("LayoutComponent comp = model.getLayoutComponent(\""
2635:                                    + comp.getId() + "\");"); //NOI18N
2636:                    testCode.add("int dimension = " + dimension); //NOI18N
2637:                    testCode.add("int alignment = " + alignment); //NOI18N 
2638:                    testCode
2639:                            .add("ld.adjustComponentAlignment(comp, dimension, alignment);"); //NOI18N
2640:                    testCode.add("}"); //NOI18N
2641:                }
2642:                modelListener.deactivate();
2643:                LayoutInterval interval = comp.getLayoutInterval(dimension);
2644:
2645:                // Skip non-resizable groups
2646:                LayoutInterval parent = interval.getParent();
2647:                while (parent != null) {
2648:                    if (!LayoutInterval.canResize(parent)) {
2649:                        interval = parent;
2650:                    }
2651:                    parent = parent.getParent();
2652:                }
2653:                assert !LayoutInterval.wantResize(interval);
2654:
2655:                boolean changed = false;
2656:                parent = interval.getParent();
2657:                while (parent != null) {
2658:                    if (parent.isParallel()) {
2659:                        if (LayoutInterval.wantResize(parent)
2660:                                && !LayoutInterval.wantResize(interval)) {
2661:                            int alg = interval.getAlignment();
2662:                            if (alg != alignment) {
2663:                                // Add fixed gap and change alignment
2664:                                int size = LayoutInterval
2665:                                        .getIntervalCurrentSize(parent,
2666:                                                dimension)
2667:                                        - LayoutInterval
2668:                                                .getIntervalCurrentSize(
2669:                                                        interval, dimension);
2670:                                if (size > 0) {
2671:                                    if (!interval.isSequential()) {
2672:                                        LayoutInterval seq = new LayoutInterval(
2673:                                                SEQUENTIAL);
2674:                                        layoutModel.setIntervalAlignment(
2675:                                                interval, DEFAULT);
2676:                                        int i = layoutModel
2677:                                                .removeInterval(interval);
2678:                                        layoutModel.addInterval(interval, seq,
2679:                                                -1);
2680:                                        layoutModel.addInterval(seq, parent, i);
2681:                                        interval = seq;
2682:                                    }
2683:                                    int index = (alg == LEADING) ? -1 : 0;
2684:                                    LayoutInterval gap = new LayoutInterval(
2685:                                            SINGLE);
2686:                                    gap.setSize(size);
2687:                                    layoutModel.addInterval(gap, interval,
2688:                                            index);
2689:                                }
2690:                                layoutModel.setIntervalAlignment(interval,
2691:                                        alignment);
2692:                            }
2693:                            changed = true;
2694:                        }
2695:                    } else {
2696:                        boolean before = true;
2697:                        boolean seqChanged = false;
2698:                        for (int i = 0; i < parent.getSubIntervalCount(); i++) {
2699:                            LayoutInterval li = parent.getSubInterval(i);
2700:                            if (li == interval) {
2701:                                before = false;
2702:                            } else if (LayoutInterval.wantResize(li)) {
2703:                                if ((before && (alignment == LEADING))
2704:                                        || (!before && (alignment == TRAILING))) {
2705:                                    assert li.isEmptySpace();
2706:                                    setIntervalResizing(li, false);
2707:                                    if (li.getPreferredSize() == 0) {
2708:                                        layoutModel.removeInterval(li);
2709:                                        i--;
2710:                                    }
2711:                                    seqChanged = true;
2712:                                }
2713:                            }
2714:                        }
2715:                        if (!changed && seqChanged) {
2716:                            boolean insertGap = false;
2717:                            int index = parent.indexOf(interval);
2718:                            if (alignment == LEADING) {
2719:                                if (parent.getSubIntervalCount() <= index + 1) {
2720:                                    insertGap = true;
2721:                                    index = -1;
2722:                                } else {
2723:                                    index++;
2724:                                    LayoutInterval candidate = parent
2725:                                            .getSubInterval(index);
2726:                                    if (candidate.isEmptySpace()) {
2727:                                        setIntervalResizing(candidate, true);
2728:                                    } else {
2729:                                        insertGap = true;
2730:                                    }
2731:                                }
2732:                            } else {
2733:                                assert (alignment == TRAILING);
2734:                                if (index == 0) {
2735:                                    insertGap = true;
2736:                                } else {
2737:                                    LayoutInterval candidate = parent
2738:                                            .getSubInterval(index - 1);
2739:                                    if (candidate.isEmptySpace()) {
2740:                                        setIntervalResizing(candidate, true);
2741:                                    } else {
2742:                                        insertGap = true;
2743:                                    }
2744:                                }
2745:                            }
2746:                            if (insertGap) {
2747:                                LayoutInterval gap = new LayoutInterval(SINGLE);
2748:                                setIntervalResizing(gap, true);
2749:                                layoutModel.setIntervalSize(gap, 0, 0, gap
2750:                                        .getMaximumSize());
2751:                                layoutModel.addInterval(gap, parent, index);
2752:                            }
2753:                            changed = true;
2754:                        }
2755:                    }
2756:                    interval = parent;
2757:                    parent = parent.getParent();
2758:                }
2759:                updateDesignModifications(interval, dimension);
2760:                modelListener.activate();
2761:                visualStateUpToDate = false;
2762:                if (logTestCode()) {
2763:                    testCode.add("// < ADJUST COMPONENT ALIGNMENT"); //NOI18N
2764:                }
2765:            }
2766:
2767:            /**
2768:             * Returns alignment of the component as the first item of the array.
2769:             * The second item of the array indicates whether the alignment can
2770:             * be changed to leading or trailing (e.g. if the current value is not
2771:             * enforced by other resizable components). The returned alignment is
2772:             * global e.g. it shows which edge of the container the component will track.
2773:             *
2774:             * @param comp component whose alignment should be determined.
2775:             * @param dimension dimension in which the alignment should be determined.
2776:             * @return alignment (or -1 if the component doesn't have a global alignment)
2777:             * as the first item of the array and (canBeChangedToLeading ? 1 : 0) +
2778:             * (canBeChangedToTrailing ? 2 : 0) as the second item.
2779:             */
2780:            public int[] getAdjustableComponentAlignment(LayoutComponent comp,
2781:                    int dimension) {
2782:                LayoutInterval interval = comp.getLayoutInterval(dimension);
2783:                boolean leadingFixed = true;
2784:                boolean trailingFixed = true;
2785:                boolean leadingAdjustable = true;
2786:                boolean trailingAdjustable = true;
2787:
2788:                if (LayoutInterval.wantResize(interval)) {
2789:                    leadingFixed = trailingFixed = leadingAdjustable = trailingAdjustable = false;
2790:                }
2791:                LayoutInterval parent = interval.getParent();
2792:                while (parent != null) {
2793:                    if (!LayoutInterval.canResize(parent)) {
2794:                        leadingFixed = trailingFixed = leadingAdjustable = trailingAdjustable = true;
2795:                    } else if (parent.isParallel()) {
2796:                        if (LayoutInterval.wantResize(parent)
2797:                                && !LayoutInterval.wantResize(interval)) {
2798:                            int alignment = interval.getAlignment();
2799:                            if (alignment == LEADING) {
2800:                                trailingFixed = false;
2801:                            } else if (alignment == TRAILING) {
2802:                                leadingFixed = false;
2803:                            }
2804:                        }
2805:                    } else {
2806:                        boolean before = true;
2807:                        Iterator iter = parent.getSubIntervals();
2808:                        while (iter.hasNext()) {
2809:                            LayoutInterval li = (LayoutInterval) iter.next();
2810:                            if (li == interval) {
2811:                                before = false;
2812:                            } else if (LayoutInterval.wantResize(li)) {
2813:                                boolean space = li.isEmptySpace();
2814:                                if (before) {
2815:                                    leadingFixed = false;
2816:                                    if (!space) {
2817:                                        leadingAdjustable = false;
2818:                                    }
2819:                                } else {
2820:                                    trailingFixed = false;
2821:                                    if (!space) {
2822:                                        trailingAdjustable = false;
2823:                                    }
2824:                                }
2825:                            }
2826:                        }
2827:                    }
2828:                    interval = parent;
2829:                    parent = parent.getParent();
2830:                }
2831:                int adjustable = (leadingAdjustable ? 1 << LEADING : 0)
2832:                        + (trailingAdjustable ? 1 << TRAILING : 0);
2833:                if (leadingFixed && trailingFixed) {
2834:                    // As if top level group wantResize()
2835:                    if (LEADING == interval.getGroupAlignment()) {
2836:                        trailingFixed = false;
2837:                    } else {
2838:                        leadingFixed = false;
2839:                    }
2840:                }
2841:                int alignment;
2842:                if (leadingFixed) {
2843:                    // !trailingFixed
2844:                    alignment = LEADING;
2845:                } else {
2846:                    if (trailingFixed) {
2847:                        alignment = TRAILING;
2848:                    } else {
2849:                        alignment = -1;
2850:                    }
2851:                }
2852:                return new int[] { alignment, adjustable };
2853:            }
2854:
2855:            /**
2856:             * Determines whether the component is resizing in the given direction.
2857:             *
2858:             * @param comp component whose resizability should be determined.
2859:             * @param dimension dimension in which the resizability should be determined.
2860:             * @return <code>true</code> if the component is resizing, returns
2861:             * <code>false</code> otherwise.
2862:             */
2863:            public boolean isComponentResizing(LayoutComponent comp,
2864:                    int dimension) {
2865:                LayoutInterval interval = comp.getLayoutInterval(dimension);
2866:                boolean fill = interval
2867:                        .hasAttribute(LayoutInterval.ATTRIBUTE_FILL);
2868:                return fill ? false : LayoutInterval
2869:                        .wantResizeInLayout(interval);
2870:            }
2871:
2872:            /**
2873:             * Returns preferred size of the given interval (in pixels).
2874:             *
2875:             * @param interval interval whose preferred size should be determined.
2876:             * @return preferred size of the given interval.
2877:             */
2878:            private int prefSizeOfInterval(LayoutInterval interval) {
2879:                int dimension = -1;
2880:                if (interval.isComponent()) {
2881:                    LayoutComponent comp = interval.getComponent();
2882:                    dimension = (interval == comp.getLayoutInterval(HORIZONTAL)) ? HORIZONTAL
2883:                            : VERTICAL;
2884:                    if (comp.isLinkSized(dimension)) {
2885:                        Collection linked = (Collection) layoutModel
2886:                                .getLinkSizeGroups(dimension).get(
2887:                                        new Integer(comp
2888:                                                .getLinkSizeId(dimension)));
2889:                        Iterator iter = linked.iterator();
2890:                        int prefSize = 0;
2891:                        while (iter.hasNext()) {
2892:                            String compId = (String) iter.next();
2893:                            LayoutComponent component = layoutModel
2894:                                    .getLayoutComponent(compId);
2895:                            LayoutInterval intr = component
2896:                                    .getLayoutInterval(dimension);
2897:                            int pref = intr.getPreferredSize();
2898:                            if (pref == NOT_EXPLICITLY_DEFINED) {
2899:                                Dimension prefDim = visualMapper
2900:                                        .getComponentPreferredSize(compId);
2901:                                pref = (dimension == HORIZONTAL) ? prefDim.width
2902:                                        : prefDim.height;
2903:                            }
2904:                            prefSize = Math.max(pref, prefSize);
2905:                        }
2906:                        return prefSize;
2907:                    }
2908:                }
2909:                int prefSize = interval.getPreferredSize();
2910:                if (prefSize == NOT_EXPLICITLY_DEFINED) {
2911:                    if (interval.isComponent()) {
2912:                        LayoutComponent comp = interval.getComponent();
2913:                        Dimension pref = visualMapper
2914:                                .getComponentPreferredSize(comp.getId());
2915:                        return (dimension == HORIZONTAL) ? pref.width
2916:                                : pref.height;
2917:                    } else if (interval.isEmptySpace()) {
2918:                        return sizeOfEmptySpace(interval);
2919:                    } else {
2920:                        assert interval.isGroup();
2921:                        prefSize = 0;
2922:                        Iterator iter = interval.getSubIntervals();
2923:                        if (interval.isSequential()) {
2924:                            while (iter.hasNext()) {
2925:                                LayoutInterval subInterval = (LayoutInterval) iter
2926:                                        .next();
2927:                                prefSize += prefSizeOfInterval(subInterval);
2928:                            }
2929:                        } else {
2930:                            while (iter.hasNext()) {
2931:                                LayoutInterval subInterval = (LayoutInterval) iter
2932:                                        .next();
2933:                                prefSize = Math.max(prefSize,
2934:                                        prefSizeOfInterval(subInterval));
2935:                            }
2936:                        }
2937:                    }
2938:                }
2939:                return prefSize;
2940:            }
2941:
2942:            /**
2943:             * Returns size of the empty space represented by the given layout interval.
2944:             *
2945:             * @param interval layout interval that represents padding.
2946:             * @return size of the padding.
2947:             */
2948:            private int sizeOfEmptySpace(LayoutInterval interval) {
2949:                return LayoutUtils.getSizeOfDefaultGap(interval, visualMapper);
2950:            }
2951:
2952:            /**
2953:             * Sets component resizability. Makes the component resizing or fixed.
2954:             *
2955:             * @param comp component whose resizability should be set.
2956:             * @param dimension dimension in which the resizability should be changed.
2957:             * @param resizable determines whether the component should be made
2958:             * resizable in the given dimension.
2959:             */
2960:            public void setComponentResizing(LayoutComponent comp,
2961:                    int dimension, boolean resizing) {
2962:                if (logTestCode()) {
2963:                    testCode.add("// > SET COMPONENT RESIZING"); //NOI18N
2964:                    testCode.add("{"); //NOI18N
2965:                    testCode
2966:                            .add("LayoutComponent comp = lm.getLayoutComponent(\""
2967:                                    + comp.getId() + "\");"); //NOI18N
2968:                    testCode.add("int dimension = " + dimension + ";"); //NOI18N
2969:                    testCode.add("boolean resizing = " + resizing + ";"); //NOI18N   
2970:                    testCode
2971:                            .add("ld.setComponentResizing(comp, dimension, resizing);"); //NOI18N
2972:                    testCode.add("}"); //NOI18N
2973:                }
2974:                modelListener.deactivate();
2975:                LayoutInterval interval = comp.getLayoutInterval(dimension);
2976:
2977:                // Unset the same-size if we are making the component resizable
2978:                if (resizing && comp.isLinkSized(dimension)) {
2979:                    Collection linked = (Collection) layoutModel
2980:                            .getLinkSizeGroups(dimension).get(
2981:                                    new Integer(comp.getLinkSizeId(dimension)));
2982:                    Collection toChange;
2983:                    if (linked.size() == 2) { // The second component will be unlinked, too.
2984:                        toChange = linked;
2985:                    } else {
2986:                        toChange = Collections.singletonList(comp.getId());
2987:                    }
2988:                    Iterator iter = toChange.iterator();
2989:                    while (iter.hasNext()) {
2990:                        String compId = (String) iter.next();
2991:                        LayoutComponent component = layoutModel
2992:                                .getLayoutComponent(compId);
2993:                        LayoutInterval intr = component
2994:                                .getLayoutInterval(dimension);
2995:                        Dimension prefDim = visualMapper
2996:                                .getComponentPreferredSize(compId);
2997:                        int prefSize = (dimension == HORIZONTAL) ? prefDim.width
2998:                                : prefDim.height;
2999:                        int currSize = intr.getCurrentSpace().size(dimension);
3000:                        if (currSize == prefSize) {
3001:                            currSize = NOT_EXPLICITLY_DEFINED;
3002:                        }
3003:                        layoutModel.setIntervalSize(intr,
3004:                                intr.getMinimumSize(), currSize, intr
3005:                                        .getMaximumSize());
3006:                    }
3007:                }
3008:
3009:                LayoutInterval parent = interval.getParent();
3010:                boolean fill = interval
3011:                        .hasAttribute(LayoutInterval.ATTRIBUTE_FILL);
3012:                boolean formerFill = interval
3013:                        .hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL);
3014:                if (fill || formerFill) {
3015:                    switchFillAttribute(interval, resizing);
3016:                } else {
3017:                    setIntervalResizing(interval, resizing);
3018:                }
3019:                int delta = 0;
3020:                if (!resizing) {
3021:                    int currSize = LayoutInterval.getIntervalCurrentSize(
3022:                            interval, dimension);
3023:                    int prefSize = prefSizeOfInterval(interval);
3024:                    delta = currSize - prefSize;
3025:                    if (delta != 0) {
3026:                        layoutModel.setIntervalSize(interval, interval
3027:                                .getMinimumSize(), currSize, interval
3028:                                .getMaximumSize());
3029:                    }
3030:                }
3031:                LayoutInterval intr = interval;
3032:                LayoutInterval par = parent;
3033:                while (par != null) {
3034:                    if (par.isParallel() && resizing) {
3035:                        int groupCurrSize = LayoutInterval
3036:                                .getIntervalCurrentSize(par, dimension);
3037:                        int currSize = LayoutInterval.getIntervalCurrentSize(
3038:                                intr, dimension);
3039:                        // PENDING currSize could change if groupPrefSize != groupCurrSize
3040:                        if (groupCurrSize != currSize) {
3041:                            LayoutInterval seqGroup = intr;
3042:                            LayoutInterval space = new LayoutInterval(SINGLE);
3043:                            space.setSize(groupCurrSize - currSize);
3044:                            int alignment = intr.getAlignment();
3045:                            int index = (alignment == LEADING) ? -1 : 0;
3046:                            if (intr.isSequential()) {
3047:                                int spaceIndex = (alignment == LEADING) ? intr
3048:                                        .getSubIntervalCount() - 1 : 0;
3049:                                LayoutInterval adjacentSpace = intr
3050:                                        .getSubInterval(spaceIndex);
3051:                                if (adjacentSpace.isEmptySpace()) {
3052:                                    int spaceSize = LayoutInterval
3053:                                            .getIntervalCurrentSize(
3054:                                                    adjacentSpace, dimension);
3055:                                    layoutModel.removeInterval(adjacentSpace);
3056:                                    space.setSize(groupCurrSize - currSize
3057:                                            + spaceSize);
3058:                                }
3059:                            } else {
3060:                                seqGroup = new LayoutInterval(SEQUENTIAL);
3061:                                layoutModel.setIntervalAlignment(intr, DEFAULT);
3062:                                seqGroup.setAlignment(alignment);
3063:                                int i = layoutModel.removeInterval(intr);
3064:                                layoutModel.addInterval(intr, seqGroup, -1);
3065:                                layoutModel.addInterval(seqGroup, par, i);
3066:                            }
3067:                            layoutModel.addInterval(space, seqGroup, index);
3068:                            seqGroup.getCurrentSpace().set(dimension,
3069:                                    par.getCurrentSpace());
3070:                        }
3071:                    } else if (par.isSequential()) {
3072:                        // Change resizability of gaps
3073:                        boolean parentSeq = (parent == par);
3074:                        List<LayoutInterval> resizableList = new LinkedList<LayoutInterval>();
3075:                        int alignment = parentSeq ? LayoutInterval
3076:                                .getEffectiveAlignment(interval) : 0;
3077:                        LayoutInterval leadingGap = null;
3078:                        LayoutInterval trailingGap = null;
3079:                        boolean afterDefining = false;
3080:                        Iterator iter = par.getSubIntervals();
3081:                        while (iter.hasNext()) {
3082:                            LayoutInterval candidate = (LayoutInterval) iter
3083:                                    .next();
3084:                            if (candidate == interval) {
3085:                                afterDefining = true;
3086:                            }
3087:                            if (candidate.isEmptySpace()) {
3088:                                if (resizing) {
3089:                                    setIntervalResizing(candidate, false);
3090:                                    int currSize = LayoutInterval
3091:                                            .getIntervalCurrentSize(candidate,
3092:                                                    dimension);
3093:                                    int prefSize = prefSizeOfInterval(candidate);
3094:                                    if (currSize != prefSize) {
3095:                                        layoutModel.setIntervalSize(candidate,
3096:                                                candidate.getMinimumSize(),
3097:                                                currSize, candidate
3098:                                                        .getMaximumSize());
3099:                                        delta += currSize - prefSize;
3100:                                    }
3101:                                } else if (parentSeq) {
3102:                                    boolean wasFill = candidate
3103:                                            .hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL);
3104:                                    boolean glue = (candidate
3105:                                            .getPreferredSize() != NOT_EXPLICITLY_DEFINED);
3106:                                    if (wasFill) {
3107:                                        trailingGap = candidate;
3108:                                    } else if ((trailingGap == null)
3109:                                            || (!trailingGap
3110:                                                    .hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL))) {
3111:                                        if (glue) {
3112:                                            trailingGap = candidate;
3113:                                        } else {
3114:                                            if (afterDefining
3115:                                                    && ((trailingGap == null) || (trailingGap
3116:                                                            .getPreferredSize() == NOT_EXPLICITLY_DEFINED))) {
3117:                                                trailingGap = candidate;
3118:                                            }
3119:                                        }
3120:                                    }
3121:                                    if ((leadingGap == null) && !afterDefining) {
3122:                                        leadingGap = candidate;
3123:                                    } else {
3124:                                        if ((wasFill && ((leadingGap == null) || (!leadingGap
3125:                                                .hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL))))
3126:                                                || glue
3127:                                                && ((leadingGap == null) || (!leadingGap
3128:                                                        .hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL) && (leadingGap
3129:                                                        .getPreferredSize() == NOT_EXPLICITLY_DEFINED)))) {
3130:                                            leadingGap = candidate;
3131:                                        }
3132:                                    }
3133:                                }
3134:                            } else {
3135:                                if (candidate.getMaximumSize() == Short.MAX_VALUE) {
3136:                                    resizableList.add(candidate);
3137:                                }
3138:                            }
3139:                        }
3140:                        if (resizableList.size() > 0) {
3141:                            iter = resizableList.iterator();
3142:                            delta = (LayoutInterval.getIntervalCurrentSize(par,
3143:                                    dimension)
3144:                                    - prefSizeOfInterval(par) + delta)
3145:                                    / resizableList.size();
3146:                            while (iter.hasNext()) {
3147:                                LayoutInterval candidate = (LayoutInterval) iter
3148:                                        .next();
3149:                                if (candidate.isGroup()) {
3150:                                    // PENDING currSize could change - we can't modify prefSize of group directly
3151:                                } else {
3152:                                    if (candidate == interval) {
3153:                                        if (delta != 0) {
3154:                                            int prefSize = prefSizeOfInterval(candidate);
3155:                                            layoutModel.setIntervalSize(
3156:                                                    candidate, candidate
3157:                                                            .getMinimumSize(),
3158:                                                    Math.max(0, prefSize
3159:                                                            - delta), candidate
3160:                                                            .getMaximumSize());
3161:                                        }
3162:                                    } else {
3163:                                        int currSize = LayoutInterval
3164:                                                .getIntervalCurrentSize(
3165:                                                        candidate, dimension);
3166:                                        layoutModel.setIntervalSize(candidate,
3167:                                                candidate.getMinimumSize(),
3168:                                                Math.max(0, currSize - delta),
3169:                                                candidate.getMaximumSize());
3170:                                    }
3171:                                }
3172:                            }
3173:                        }
3174:                        if (parentSeq) {
3175:                            if (!LayoutInterval.wantResize(par)) {
3176:                                LayoutInterval gap = null;
3177:                                if ((alignment == TRAILING)
3178:                                        && (leadingGap != null)) {
3179:                                    gap = leadingGap;
3180:                                    setIntervalResizing(leadingGap, !resizing);
3181:                                    layoutModel
3182:                                            .changeIntervalAttribute(
3183:                                                    leadingGap,
3184:                                                    LayoutInterval.ATTRIBUTE_FILL,
3185:                                                    true);
3186:                                }
3187:                                if ((alignment == LEADING)
3188:                                        && (trailingGap != null)) {
3189:                                    gap = trailingGap;
3190:                                    setIntervalResizing(trailingGap, !resizing);
3191:                                    layoutModel
3192:                                            .changeIntervalAttribute(
3193:                                                    trailingGap,
3194:                                                    LayoutInterval.ATTRIBUTE_FILL,
3195:                                                    true);
3196:                                }
3197:                                if ((gap != null)
3198:                                        && (delta != 0)
3199:                                        && (gap.getPreferredSize() != NOT_EXPLICITLY_DEFINED)) {
3200:                                    layoutModel.setIntervalSize(gap, gap
3201:                                            .getMinimumSize(), Math.max(0, gap
3202:                                            .getPreferredSize()
3203:                                            - delta), gap.getMaximumSize());
3204:                                }
3205:                            }
3206:                            parent = par.getParent(); // use parallel parent for group resizing check
3207:                        }
3208:                    }
3209:                    intr = par;
3210:                    par = par.getParent();
3211:                }
3212:
3213:                // Unset the same size once all changes in gap sizes are done
3214:                if (resizing) {
3215:                    layoutModel.unsetSameSize(Collections.singletonList(comp
3216:                            .getId()), dimension);
3217:                }
3218:                modelListener.activate();
3219:
3220:                if (resizing) {
3221:                    // cancel possible suppressed resizing
3222:                    while (parent != null) {
3223:                        if (!LayoutInterval.canResize(parent)) {
3224:                            operations.enableGroupResizing(parent);
3225:                        }
3226:                        parent = parent.getParent();
3227:                    }
3228:                } else {
3229:                    // check if we should suppress resizing
3230:                    while (parent != null) {
3231:                        if (fillResizable(parent)) {
3232:                            operations.suppressGroupResizing(parent);
3233:                            parent = parent.getParent();
3234:                        } else {
3235:                            break;
3236:                        }
3237:                    }
3238:                }
3239:
3240:                updateDesignModifications(comp.getParent());
3241:                visualStateUpToDate = false;
3242:                if (logTestCode()) {
3243:                    testCode.add("// < SET COMPONENT RESIZING"); //NOI18N
3244:                }
3245:            }
3246:
3247:            private boolean fillResizable(LayoutInterval interval) {
3248:                if (!LayoutInterval.canResize(interval)) {
3249:                    return false;
3250:                }
3251:                if (interval.isGroup()) {
3252:                    boolean subres = true;
3253:                    Iterator it = interval.getSubIntervals();
3254:                    while (it.hasNext()) {
3255:                        LayoutInterval li = (LayoutInterval) it.next();
3256:                        if (LayoutInterval.wantResize(li) && !fillResizable(li)) {
3257:                            subres = false;
3258:                            break;
3259:                        }
3260:                    }
3261:                    return subres;
3262:                } else {
3263:                    return interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL);
3264:                }
3265:            }
3266:
3267:            /**
3268:             * Aligns given components in the specified direction.
3269:             *
3270:             * @param componentIds IDs of components that should be aligned.
3271:             * @param closed determines if closed group should be created.
3272:             * @param dimension dimension to align in.
3273:             * @param alignment requested alignment.
3274:             */
3275:            public void align(Collection componentIds, boolean closed,
3276:                    int dimension, int alignment) {
3277:                if (logTestCode()) {
3278:                    testCode.add("// > ALIGN"); //NOI18N
3279:                    testCode.add("{"); //NOI18N
3280:                    LayoutTestUtils.writeCollection(testCode, "componentIds",
3281:                            componentIds); //NOI18N
3282:                    testCode.add("boolean closed = " + closed + ";"); //NOI18N
3283:                    testCode.add("int dimension = " + dimension + ";"); //NOI18N   
3284:                    testCode.add("int alignment = " + alignment + ";"); //NOI18N  
3285:                    testCode
3286:                            .add("ld.align(componentIds, closed, dimension, alignment);"); //NOI18N
3287:                    testCode.add("}"); //NOI18N
3288:                }
3289:                LayoutInterval[] intervals = new LayoutInterval[componentIds
3290:                        .size()];
3291:                int counter = 0;
3292:                Iterator iter = componentIds.iterator();
3293:                while (iter.hasNext()) {
3294:                    String id = (String) iter.next();
3295:                    LayoutComponent component = layoutModel
3296:                            .getLayoutComponent(id);
3297:                    intervals[counter++] = component
3298:                            .getLayoutInterval(dimension);
3299:                }
3300:                modelListener.deactivate();
3301:                try {
3302:                    new LayoutAligner(this , layoutModel, operations)
3303:                            .alignIntervals(intervals, closed, dimension,
3304:                                    alignment);
3305:                } finally {
3306:                    modelListener.activate();
3307:                }
3308:                requireStructureOptimization();
3309:                if (logTestCode()) {
3310:                    testCode.add("// < ALIGN"); //NOI18N
3311:                }
3312:            }
3313:
3314:            private void destroyRedundantGroups(
3315:                    Set<LayoutComponent> updatedContainers) {
3316:                Iterator it = layoutModel.getAllComponents();
3317:                while (it.hasNext()) {
3318:                    LayoutComponent comp = (LayoutComponent) it.next();
3319:                    if (!comp.isLayoutContainer())
3320:                        continue;
3321:
3322:                    boolean updated = false;
3323:                    for (LayoutInterval[] roots : comp.getLayoutRoots()) {
3324:                        for (int dim = 0; dim < DIM_COUNT; dim++) {
3325:                            updated = destroyRedundantGroups(roots[dim])
3326:                                    || updated;
3327:                        }
3328:                    }
3329:                    if (updated) {
3330:                        updatedContainers.add(comp);
3331:                    }
3332:                }
3333:            }
3334:
3335:            private boolean destroyRedundantGroups(LayoutInterval interval) {
3336:                boolean updated = false;
3337:                for (int i = interval.getSubIntervalCount() - 1; i >= 0; i--) {
3338:                    if (i >= interval.getSubIntervalCount())
3339:                        continue;
3340:                    LayoutInterval subInterval = interval.getSubInterval(i);
3341:                    if (subInterval.isGroup()) {
3342:                        destroyRedundantGroups(subInterval);
3343:                        destroyGroupIfRedundant(subInterval, interval);
3344:                        updated |= (subInterval.getParent() == null);
3345:                    }
3346:                }
3347:                return updated;
3348:            }
3349:
3350:            /**
3351:             * Destroys the given group if it is redundant in the layout model.
3352:             *
3353:             * @param group group whose necessity should be checked.
3354:             * @param boundary parent of the group that limits the changes that
3355:             * should be made e.g. no changes outside of this group even if it were
3356:             * itself redundant. Can be <code>null</code> if there's no boundary.
3357:             */
3358:            void destroyGroupIfRedundant(LayoutInterval group,
3359:                    LayoutInterval boundary) {
3360:                if ((group == null) || (!group.isGroup())
3361:                        || (group == boundary))
3362:                    return;
3363:                LayoutInterval parent = group.getParent();
3364:                // Don't destroy root intervals
3365:                if (parent == null)
3366:                    return;
3367:
3368:                // Remove empty groups
3369:                if (LayoutInterval.getCount(group, LayoutRegion.ALL_POINTS,
3370:                        true) == 0) {
3371:                    takeOutInterval(group, boundary);
3372:                    return;
3373:                }
3374:
3375:                if (operations.dissolveRedundantGroup(group)) {
3376:                    destroyGroupIfRedundant(parent, boundary);
3377:                }
3378:            }
3379:
3380:            /**
3381:             * Removes the given interval from the layout model. Consolidates
3382:             * parent groups if necessary.
3383:             *
3384:             * @param interval interval that should be removed.
3385:             * @param boundary parent of the group that limits the changes that
3386:             * should be made e.g. no changes outside of this group even if it were
3387:             * itself redundant. Can be <code>null</code> if there's no boundary.
3388:             */
3389:            void takeOutInterval(LayoutInterval interval,
3390:                    LayoutInterval boundary) {
3391:                LayoutInterval parent = interval.getParent();
3392:                int index = parent.indexOf(interval);
3393:                List<LayoutInterval> toRemove = new LinkedList<LayoutInterval>();
3394:                toRemove.add(interval);
3395:                if (parent.isSequential()) {
3396:                    // Remove leading gap
3397:                    if (index > 0) {
3398:                        LayoutInterval li = parent.getSubInterval(index - 1);
3399:                        if (li.isEmptySpace()) {
3400:                            toRemove.add(li);
3401:                        }
3402:                    }
3403:                    // Remove trailing gap
3404:                    if (index + 1 < parent.getSubIntervalCount()) {
3405:                        LayoutInterval li = parent.getSubInterval(index + 1);
3406:                        if (li.isEmptySpace()) {
3407:                            toRemove.add(li);
3408:                        }
3409:                    }
3410:                    // Add dummy gap if necessary
3411:                    if ((toRemove.size() == 3)
3412:                            && (parent.getSubIntervalCount() > 3)) {
3413:                        LayoutInterval gap = new LayoutInterval(SINGLE);
3414:                        if (interval.isComponent()
3415:                                && (interval.getComponent().getLayoutInterval(
3416:                                        VERTICAL) == interval)) {
3417:                            int alignment = LayoutInterval
3418:                                    .getEffectiveAlignment(interval);
3419:                            int size = 0;
3420:                            for (int i = 0; i < 3; i++) {
3421:                                size += LayoutInterval.getIntervalCurrentSize(
3422:                                        toRemove.get(i), VERTICAL);
3423:                            }
3424:                            gap.setSizes(NOT_EXPLICITLY_DEFINED, size,
3425:                                    (alignment == TRAILING) ? Short.MAX_VALUE
3426:                                            : USE_PREFERRED_SIZE);
3427:                        }
3428:                        layoutModel.addInterval(gap, parent, index);
3429:                    }
3430:                }
3431:                Iterator iter = toRemove.iterator();
3432:                while (iter.hasNext()) {
3433:                    LayoutInterval remove = (LayoutInterval) iter.next();
3434:                    layoutModel.removeInterval(remove);
3435:                }
3436:                // Consolidate parent
3437:                destroyGroupIfRedundant(parent, boundary);
3438:            }
3439:
3440:            // -----
3441:
3442:            // [should change to operations.addGroupContent]
3443:            /**
3444:             * Creates a remainder parallel group (remainder to a main group of
3445:             * aligned intervals).
3446:             * @param list the content of the group, output from 'extract' method
3447:             * @param seq a sequential group where to add to
3448:             * @param index the index of the main group in the sequence
3449:             * @param position the position of the remainder group relative to the main
3450:             *        group (LEADING or TRAILING)
3451:             * @param mainAlignment effective alignment of the main group (LEADING or
3452:             *        TRAILING or something else meaning not aligned)
3453:             * @param dimension dimension the remainder group is created in.
3454:             */
3455:            void createRemainderGroup(List list, LayoutInterval seq, int index,
3456:                    int position, int mainAlignment, int dimension) {
3457:                assert seq.isSequential()
3458:                        && (position == LEADING || position == TRAILING);
3459:                if (position == TRAILING) {
3460:                    index++;
3461:                }
3462:                // [revisit the way how spaces are handled - in accordance to optimizeGaps]
3463:
3464:                LayoutInterval gap = null;
3465:                LayoutInterval leadingGap = null;
3466:                LayoutInterval trailingGap = null;
3467:                boolean onlyGaps = true;
3468:                boolean gapLeads = true;
3469:                boolean gapTrails = true;
3470:
3471:                // Remove sequences just with one gap
3472:                for (int i = list.size() - 1; i >= 0; i--) {
3473:                    List subList = (List) list.get(i);
3474:                    if (subList.size() == 2) { // there is just one interval
3475:                        int alignment = ((Integer) subList.get(0)).intValue();
3476:                        LayoutInterval li = (LayoutInterval) subList.get(1);
3477:                        if (li.isEmptySpace()) {
3478:                            if (gap == null
3479:                                    || li.getMaximumSize() > gap
3480:                                            .getMaximumSize()) {
3481:                                gap = li;
3482:                            }
3483:                            if (isFixedPadding(li)) {
3484:                                if (alignment == LEADING) {
3485:                                    leadingGap = li;
3486:                                    gapTrails = false;
3487:                                } else if (alignment == TRAILING) {
3488:                                    trailingGap = li;
3489:                                    gapLeads = false;
3490:                                }
3491:                            } else {
3492:                                gapLeads = false;
3493:                                gapTrails = false;
3494:                            }
3495:                            list.remove(i);
3496:                        } else {
3497:                            onlyGaps = false;
3498:                        }
3499:                    }
3500:                }
3501:
3502:                if (list.size() == 1) { // just one sequence, need not a group
3503:                    List subList = (List) list.get(0);
3504:                    Iterator itr = subList.iterator();
3505:                    itr.next(); // skip alignment
3506:                    do {
3507:                        LayoutInterval li = (LayoutInterval) itr.next();
3508:                        layoutModel.addInterval(li, seq, index++);
3509:                    } while (itr.hasNext());
3510:                    return;
3511:                }
3512:
3513:                // find common ending gaps, possibility to eliminate some...
3514:                for (Iterator it = list.iterator(); it.hasNext();) {
3515:                    List subList = (List) it.next();
3516:                    if (subList.size() != 2) { // there are more intervals (will form a sequential group)
3517:                        onlyGaps = false;
3518:
3519:                        boolean first = true;
3520:                        Iterator itr = subList.iterator();
3521:                        itr.next(); // skip seq. alignment
3522:                        do {
3523:                            LayoutInterval li = (LayoutInterval) itr.next();
3524:                            if (first) {
3525:                                first = false;
3526:                                if (isFixedPadding(li))
3527:                                    leadingGap = li;
3528:                                else
3529:                                    gapLeads = false;
3530:                            } else if (!itr.hasNext()) {
3531:                                if (isFixedPadding(li))
3532:                                    trailingGap = li;
3533:                                else
3534:                                    gapTrails = false;
3535:                            }
3536:                        } while (itr.hasNext());
3537:                    }
3538:                }
3539:
3540:                if (onlyGaps) {
3541:                    operations
3542:                            .insertGapIntoSequence(gap, seq, index, dimension);
3543:                    return;
3544:                }
3545:
3546:                // create group
3547:                LayoutInterval group = new LayoutInterval(PARALLEL);
3548:                if (position == mainAlignment) {
3549:                    // [but this should eliminate resizability only for gaps...]
3550:                    group.setMinimumSize(USE_PREFERRED_SIZE);
3551:                    group.setMaximumSize(USE_PREFERRED_SIZE);
3552:                }
3553:                //        group.setGroupAlignment(alignment);
3554:
3555:                // fill the group
3556:                for (Iterator it = list.iterator(); it.hasNext();) {
3557:                    List subList = (List) it.next();
3558:
3559:                    if (gapLeads) {
3560:                        subList.remove(1);
3561:                    }
3562:                    if (gapTrails) {
3563:                        subList.remove(subList.size() - 1);
3564:                    }
3565:
3566:                    LayoutInterval interval;
3567:                    if (subList.size() == 2) { // there is just one interval - use it directly
3568:                        int alignment = ((Integer) subList.get(0)).intValue();
3569:                        interval = (LayoutInterval) subList.get(1);
3570:                        if (alignment == LEADING || alignment == TRAILING) {
3571:                            layoutModel.setIntervalAlignment(interval,
3572:                                    alignment);
3573:                        }
3574:                    } else { // there are more intervals - group them in a sequence
3575:                        interval = new LayoutInterval(SEQUENTIAL);
3576:                        Iterator itr = subList.iterator();
3577:                        int alignment = ((Integer) itr.next()).intValue();
3578:                        if (alignment == LEADING || alignment == TRAILING) {
3579:                            interval.setAlignment(alignment);
3580:                        }
3581:                        do {
3582:                            LayoutInterval li = (LayoutInterval) itr.next();
3583:                            layoutModel.addInterval(li, interval, -1);
3584:                        } while (itr.hasNext());
3585:                    }
3586:                    layoutModel.addInterval(interval, group, -1);
3587:                }
3588:
3589:                // add the group to the sequence
3590:                if (gapLeads) {
3591:                    layoutModel.addInterval(leadingGap, seq, index++);
3592:                }
3593:                layoutModel.addInterval(group, seq, index++);
3594:                if (gapTrails) {
3595:                    layoutModel.addInterval(trailingGap, seq, index);
3596:                }
3597:            }
3598:
3599:            static boolean isFixedPadding(LayoutInterval interval) {
3600:                return interval.isEmptySpace()
3601:                        && (interval.getMinimumSize() == NOT_EXPLICITLY_DEFINED || interval
3602:                                .getMinimumSize() == USE_PREFERRED_SIZE)
3603:                        && interval.getPreferredSize() == NOT_EXPLICITLY_DEFINED
3604:                        && (interval.getMaximumSize() == NOT_EXPLICITLY_DEFINED || interval
3605:                                .getMaximumSize() == USE_PREFERRED_SIZE);
3606:            }
3607:
3608:            // -----
3609:
3610:            // requires the layout image up-to-date (all positions known)
3611:            // requires the group contains some component (at least indirectly)
3612:            private int optimizeGaps(LayoutInterval group, int dimension,
3613:                    boolean recursive) {
3614:                assert group.isParallel();
3615:
3616:                // sub-groups first (not using iterator, intervals may change)
3617:                if (recursive) {
3618:                    for (int i = 0; i < group.getSubIntervalCount(); i++) {
3619:                        LayoutInterval li = group.getSubInterval(i);
3620:                        if (li.isParallel()) {
3621:                            optimizeGaps(li, dimension, recursive);
3622:                        } else if (li.isSequential()) {
3623:                            for (int ii = 0; ii < li.getSubIntervalCount(); ii++) {
3624:                                LayoutInterval llii = li.getSubInterval(ii);
3625:                                if (llii.isParallel()) {
3626:                                    int idx = optimizeGaps(llii, dimension,
3627:                                            recursive);
3628:                                    if (idx >= 0) // position in sequence changed (a gap inserted)
3629:                                        ii = idx;
3630:                                }
3631:                            }
3632:                        }
3633:                    }
3634:                }
3635:
3636:                if (group.getGroupAlignment() == CENTER
3637:                        || group.getGroupAlignment() == BASELINE) {
3638:                    return -1;
3639:                }
3640:                int nonEmptyCount = LayoutInterval.getCount(group,
3641:                        LayoutRegion.ALL_POINTS, true);
3642:                if (nonEmptyCount <= 1) {
3643:                    if (group.getParent() == null) {
3644:                        if (group.getSubIntervalCount() > 1) {
3645:                            // [removing container supporting gap]
3646:                            for (int i = group.getSubIntervalCount() - 1; i >= 0; i--) {
3647:                                if (group.getSubInterval(i).isEmptySpace()) {
3648:                                    layoutModel.removeInterval(group, i);
3649:                                    break;
3650:                                }
3651:                            }
3652:                        } else if (group.getSubIntervalCount() == 0) {
3653:                            // [sort of hack - would be nice the filling gap is ensured somewhere else]
3654:                            propEmptyContainer(group, dimension);
3655:                        }
3656:                    } else { // [dissolving one-member group should not be here]
3657:                        assert (nonEmptyCount == 1);
3658:                        assert (group.getSubIntervalCount() == 1);
3659:                        LayoutInterval interval = group.getSubInterval(0);
3660:                        //int alignment = interval.getAlignment();
3661:                        layoutModel.removeInterval(interval);
3662:                        layoutModel.setIntervalAlignment(interval, group
3663:                                .getAlignment());
3664:                        LayoutInterval parent = group.getParent();
3665:                        int index = layoutModel.removeInterval(group);
3666:                        if (parent.isSequential() && interval.isSequential()) {
3667:                            // dissolve the sequential group in its parent                    
3668:                            for (int i = interval.getSubIntervalCount() - 1; i >= 0; i--) {
3669:                                LayoutInterval subInterval = interval
3670:                                        .getSubInterval(i);
3671:                                layoutModel.removeInterval(subInterval);
3672:                                layoutModel.addInterval(subInterval, parent,
3673:                                        index);
3674:                            }
3675:                            eliminateConsecutiveGaps(parent, 0, dimension);
3676:                        } else {
3677:                            layoutModel.addInterval(interval, parent, index);
3678:                        }
3679:                        /*                if (offsetGap != null) {
3680:                         // Reinsert/simulate behaviour of the removed offset gap.
3681:                         int size;
3682:                         if (parent.isSequential()) {
3683:                         size = LayoutInterval.getIntervalCurrentSize(group, dimension)
3684:                         - LayoutInterval.getIntervalCurrentSize(interval, dimension);
3685:                         if (alignment == LEADING) index++;
3686:                         } else {
3687:                         size = LayoutInterval.getIntervalCurrentSize(parent, dimension);
3688:                         index = -1;
3689:                         }
3690:                         layoutModel.setIntervalSize(offsetGap, offsetGap.getMinimumSize(), size, offsetGap.getMaximumSize());
3691:                         layoutModel.addInterval(offsetGap, parent, index);
3692:                         eliminateConsecutiveGaps(parent, 0, dimension);
3693:                         } */
3694:                    }
3695:                    return -1;
3696:                }
3697:
3698:                return operations.optimizeGaps(group, dimension);
3699:            }
3700:
3701:            // -----
3702:
3703:            /**
3704:             * Sets interval resizability. Makes it resizing or fixed.
3705:             *
3706:             * @param interval layout interval whose resizability should be set.
3707:             * @param resizable determines whether the passed interval should be made resizable.
3708:             */
3709:            void setIntervalResizing(LayoutInterval interval, boolean resizable) {
3710:                switchFillAttribute(interval, resizable);
3711:                layoutModel
3712:                        .setIntervalSize(interval,
3713:                                resizable ? NOT_EXPLICITLY_DEFINED
3714:                                        : USE_PREFERRED_SIZE, interval
3715:                                        .getPreferredSize(),
3716:                                resizable ? Short.MAX_VALUE
3717:                                        : USE_PREFERRED_SIZE);
3718:            }
3719:
3720:            // Changes fill attributes when interval becomes (non-)resizable.
3721:            private void switchFillAttribute(LayoutInterval interval,
3722:                    boolean resizable) {
3723:                if (resizable) {
3724:                    if (interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL)) {
3725:                        layoutModel.changeIntervalAttribute(interval,
3726:                                LayoutInterval.ATTRIBUTE_FORMER_FILL, true);
3727:                        layoutModel.changeIntervalAttribute(interval,
3728:                                LayoutInterval.ATTRIBUTE_FILL, false);
3729:                    }
3730:                } else {
3731:                    if (interval
3732:                            .hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL)) {
3733:                        layoutModel.changeIntervalAttribute(interval,
3734:                                LayoutInterval.ATTRIBUTE_FORMER_FILL, false);
3735:                        layoutModel.changeIntervalAttribute(interval,
3736:                                LayoutInterval.ATTRIBUTE_FILL, true);
3737:                    }
3738:                }
3739:            }
3740:
3741:            // -----
3742:
3743:            public void setDefaultSize(String compId) {
3744:                if (logTestCode()) {
3745:                    testCode.add("// > SET DEFAULT SIZE"); //NOI18N
3746:                    testCode.add("{"); //NOI18N
3747:                    testCode.add("String compId = \"${" + compId + "}\";"); //NOI18N
3748:                    testCode.add("ld.setDefaultSize(compId);"); //NOI18N
3749:                    testCode.add("}"); //NOI18N
3750:                }
3751:                LayoutComponent component = layoutModel
3752:                        .getLayoutComponent(compId);
3753:                if (component != null)
3754:                    setDefaultSize(component);
3755:                if (logTestCode()) {
3756:                    testCode.add("// < SET DEFAULT SIZE"); //NOI18N
3757:                }
3758:            }
3759:
3760:            private void setDefaultSize(LayoutComponent component) {
3761:                imposeSize = true;
3762:                if (component.isLayoutContainer()) {
3763:                    for (LayoutComponent comp : component.getSubcomponents()) {
3764:                        if (comp.isLayoutContainer())
3765:                            setDefaultSize(comp);
3766:                    }
3767:                    for (LayoutInterval[] roots : component.getLayoutRoots()) {
3768:                        for (int dim = 0; dim < DIM_COUNT; dim++) {
3769:                            setDefaultSizeInContainer(roots[dim]);
3770:                        }
3771:                    }
3772:                    updateDesignModifications(component);
3773:                } else {
3774:                    operations.resizeInterval(component
3775:                            .getLayoutInterval(HORIZONTAL),
3776:                            NOT_EXPLICITLY_DEFINED);
3777:                    operations.resizeInterval(component
3778:                            .getLayoutInterval(VERTICAL),
3779:                            NOT_EXPLICITLY_DEFINED);
3780:                }
3781:            }
3782:
3783:            private void setDefaultSizeInContainer(LayoutInterval interval) {
3784:                if (!interval.isGroup()) {
3785:                    if (LayoutInterval.canResize(interval))
3786:                        operations
3787:                                .resizeInterval(
3788:                                        interval,
3789:                                        interval.getMinimumSize() != USE_PREFERRED_SIZE ? interval
3790:                                                .getMinimumSize()
3791:                                                : NOT_EXPLICITLY_DEFINED);
3792:                } else {
3793:                    for (Iterator it = interval.getSubIntervals(); it.hasNext();) {
3794:                        setDefaultSizeInContainer((LayoutInterval) it.next());
3795:                    }
3796:                }
3797:            }
3798:
3799:            private void updateDesignModifications(LayoutComponent container) {
3800:                if (imposeSize || optimizeStructure) {
3801:                    // additional update is going to happen after building the layout, so now
3802:                    // just clean the design attrs, container resizing gap will be found later
3803:                    LayoutInterval[] roots = getActiveLayoutRoots(container);
3804:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
3805:                        cleanDesignAttrs(roots[dim]);
3806:                    }
3807:                } else {
3808:                    LayoutInterval[] roots = getActiveLayoutRoots(container);
3809:                    for (int dim = 0; dim < DIM_COUNT; dim++) {
3810:                        updateDesignModifications(roots[dim], dim);
3811:                    }
3812:                }
3813:            }
3814:
3815:            private void updateDesignModifications(LayoutInterval root,
3816:                    int dimension) {
3817:                cleanDesignAttrs(root);
3818:                findContainerResizingGap(root, dimension);
3819:            }
3820:
3821:            private static void cleanDesignAttrs(LayoutInterval group) {
3822:                group.unsetAttribute(LayoutInterval.DESIGN_ATTRS);
3823:                for (int i = 0, n = group.getSubIntervalCount(); i < n; i++) {
3824:                    LayoutInterval li = group.getSubInterval(i);
3825:                    if (li.isGroup())
3826:                        cleanDesignAttrs(li);
3827:                    else
3828:                        li.unsetAttribute(LayoutInterval.DESIGN_ATTRS);
3829:                }
3830:            }
3831:
3832:            private void findContainerResizingGap(LayoutInterval rootInterval,
3833:                    int dimension) {
3834:                if (!LayoutInterval.wantResize(rootInterval) && // See issue 66849
3835:                        (LayoutInterval.getIntervalCurrentSize(rootInterval,
3836:                                dimension) != prefSizeOfInterval(rootInterval))) {
3837:                    // Resizing gap would change the layout
3838:                    return;
3839:                }
3840:                // find gap for container resizing
3841:                int gapPosition = TRAILING;
3842:                LayoutInterval resGap = findContainerResizingGap(rootInterval,
3843:                        dimension, gapPosition);
3844:                if (resGap == null) {
3845:                    gapPosition = LEADING;
3846:                    resGap = findContainerResizingGap(rootInterval, dimension,
3847:                            gapPosition);
3848:                    if (resGap == null) {
3849:                        gapPosition = -1;
3850:                        resGap = findContainerResizingGap(rootInterval,
3851:                                dimension, gapPosition);
3852:                        if (resGap == null) {
3853:                            return;
3854:                        }
3855:                    }
3856:                } else if (!LayoutInterval.canResize(resGap)) { // we prefer resizing gaps
3857:                    LayoutInterval gap = findContainerResizingGap(rootInterval,
3858:                            dimension, LEADING);
3859:                    if (gap != null && LayoutInterval.canResize(gap)) {
3860:                        resGap = gap;
3861:                        gapPosition = LEADING;
3862:                    } else {
3863:                        gap = findContainerResizingGap(rootInterval, dimension,
3864:                                -1);
3865:                        if (gap != null && LayoutInterval.canResize(gap)) {
3866:                            resGap = gap;
3867:                            gapPosition = -1;
3868:                        }
3869:                    }
3870:                }
3871:
3872:                // mark the gap and all surrounding intervals
3873:                resGap.setAttribute(LayoutInterval.ATTR_DESIGN_CONTAINER_GAP
3874:                        | LayoutInterval.ATTR_DESIGN_RESIZING);
3875:
3876:                LayoutInterval sub = resGap;
3877:                LayoutInterval parent = resGap.getParent();
3878:                do {
3879:                    if (parent.isSequential()) {
3880:                        for (Iterator it = parent.getSubIntervals(); it
3881:                                .hasNext();) {
3882:                            LayoutInterval li = (LayoutInterval) it.next();
3883:                            if (li != sub) {
3884:                                li
3885:                                        .setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING);
3886:                            }
3887:                        }
3888:                    } else { // parallel parent
3889:                        for (Iterator it = parent.getSubIntervals(); it
3890:                                .hasNext();) {
3891:                            LayoutInterval interval = (LayoutInterval) it
3892:                                    .next();
3893:                            if (interval != sub) {
3894:                                assert interval.isSequential();
3895:                                if (interval.isSequential()) {
3896:                                    for (int i = 0, n = interval
3897:                                            .getSubIntervalCount(); i < n; i++) {
3898:                                        LayoutInterval li = interval
3899:                                                .getSubInterval(i);
3900:                                        if (((i == 0 && gapPosition == LEADING) || (i + 1 == n && gapPosition == TRAILING))
3901:                                                && canBeContainerResizingGap(li)) { // parallel resizing gap
3902:                                            li
3903:                                                    .setAttribute(LayoutInterval.ATTR_DESIGN_RESIZING);
3904:                                        } else
3905:                                            li
3906:                                                    .setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING);
3907:                                    }
3908:                                } else {
3909:                                    interval
3910:                                            .setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING);
3911:                                }
3912:                            }
3913:                        }
3914:                    }
3915:                    sub = parent;
3916:                    parent = sub.getParent();
3917:                } while (parent != null);
3918:            }
3919:
3920:            private static LayoutInterval findContainerResizingGap(
3921:                    LayoutInterval group, int dimension, int alignment) {
3922:                assert group.isParallel();
3923:
3924:                LayoutInterval theGap = null;
3925:                int gapSize = Integer.MAX_VALUE;
3926:
3927:                for (Iterator it = group.getSubIntervals(); it.hasNext();) {
3928:                    LayoutInterval seq = (LayoutInterval) it.next();
3929:                    if (!seq.isSequential()) {
3930:                        return null;
3931:                    }
3932:
3933:                    int n = seq.getSubIntervalCount();
3934:                    if (alignment == LEADING || alignment == TRAILING) {
3935:                        // [check for the same resizability of the ending gaps]
3936:                        LayoutInterval li = seq
3937:                                .getSubInterval(alignment == LEADING ? 0
3938:                                        : n - 1);
3939:                        LayoutInterval gap;
3940:                        if (canBeContainerResizingGap(li)
3941:                                && (LayoutInterval.wantResize(seq) || LayoutInterval
3942:                                        .getEffectiveAlignment(li) == (alignment ^ 1))) { // making this gap resizing won't change the visual appearance of the sequence
3943:                            gap = li;
3944:                        } else if (li.isParallel()) {
3945:                            gap = findContainerResizingGap(li, dimension,
3946:                                    alignment);
3947:                        } else
3948:                            gap = null; // not an ending gap
3949:
3950:                        if (gap == null) {
3951:                            return null;
3952:                        }
3953:
3954:                        LayoutInterval neighbor = LayoutInterval
3955:                                .getDirectNeighbor(gap, alignment ^ 1, false);
3956:                        int p1 = neighbor.getCurrentSpace().positions[dimension][alignment];
3957:                        int p2 = group.getCurrentSpace().positions[dimension][alignment];
3958:                        int size = Math.abs(p2 - p1);
3959:
3960:                        if (theGap == null || size < gapSize) {
3961:                            theGap = gap;
3962:                            gapSize = size;
3963:                        }
3964:                        //                if (LayoutInterval.canResize(gap) != LayoutInterval.canResize(theGap)) {
3965:                        //                    return null;
3966:                        //                }
3967:                    } else { // somewhere in the middle - can be just one
3968:                        for (int i = n - 2; i > 0; i--) {
3969:                            LayoutInterval li = seq.getSubInterval(i);
3970:                            if (canBeContainerResizingGap(li)
3971:                                    && LayoutInterval.canResize(li)) {
3972:                                return group.getSubIntervalCount() == 1 ? li
3973:                                        : null;
3974:                            }
3975:                        }
3976:                        return null;
3977:                    }
3978:                }
3979:
3980:                return theGap;
3981:            }
3982:
3983:            private static boolean canBeContainerResizingGap(LayoutInterval li) {
3984:                return li.isEmptySpace()
3985:                        && (li.getPreferredSize() != NOT_EXPLICITLY_DEFINED || li
3986:                                .getMaximumSize() >= Short.MAX_VALUE);
3987:            }
3988:
3989:            /**
3990:             * @param resizingDef if provided the size change is caused "internally"
3991:             *        (mouse operation driven by LayoutDragger)
3992:             */
3993:            private boolean imposeCurrentContainerSize(
3994:                    LayoutComponent component,
3995:                    LayoutDragger.SizeDef[] resizingDef, boolean recursive) {
3996:                assert component.isLayoutContainer();
3997:
3998:                Rectangle interior = visualMapper
3999:                        .getContainerInterior(component.getId());
4000:                if (interior == null)
4001:                    return false; // this container is not built
4002:                component.setCurrentInterior(interior);
4003:
4004:                if (component.getParent() != null) {
4005:                    Rectangle bounds = visualMapper
4006:                            .getComponentBounds(component.getId());
4007:                    component.setCurrentBounds(bounds, visualMapper
4008:                            .getBaselinePosition(component.getId(),
4009:                                    bounds.width, bounds.height));
4010:                }
4011:                for (LayoutComponent subComp : component.getSubcomponents()) {
4012:                    Rectangle bounds = visualMapper.getComponentBounds(subComp
4013:                            .getId());
4014:                    subComp.setCurrentBounds(bounds, visualMapper
4015:                            .getBaselinePosition(subComp.getId(), bounds.width,
4016:                                    bounds.height));
4017:                    if (subComp.isLayoutContainer()) {
4018:                        if (recursive)
4019:                            imposeCurrentContainerSize(subComp, null, true);
4020:                    } else
4021:                        imposeCurrentComponentSize(subComp);
4022:                }
4023:                Dimension minimum = null;
4024:                Dimension preferred = null;
4025:                for (int i = 0; i < DIM_COUNT; i++) {
4026:                    LayoutInterval outer = component.getLayoutInterval(i);
4027:                    int currentSize = outer.getCurrentSpace().size(i);
4028:                    LayoutInterval defaultRoot = getActiveLayoutRoots(component)[i];
4029:                    for (LayoutInterval[] roots : component.getLayoutRoots()) {
4030:                        LayoutInterval root = roots[i];
4031:                        boolean empty = root.getSubIntervalCount() == 0;
4032:                        if (root == defaultRoot) {
4033:                            if (empty) { // empty root group - add a filling gap
4034:                                propEmptyContainer(root, i);
4035:                            } else if (resizingDef != null
4036:                                    && resizingDef[i] != null) {
4037:                                // not empty, there can be a resizing gap inside the container
4038:                                LayoutInterval resGap = resizingDef[i]
4039:                                        .getResizingGap();
4040:                                if (resGap != null) { // this is it (special gap for design time resizing)
4041:                                    int size = resizingDef[i]
4042:                                            .getResizingGapSize(currentSize);
4043:                                    if (size == 0) { // remove the gap
4044:                                        LayoutInterval gapParent = resGap
4045:                                                .getParent();
4046:                                        assert gapParent.isSequential();
4047:                                        int index = layoutModel
4048:                                                .removeInterval(resGap);
4049:                                        assert index == 0
4050:                                                || index == gapParent
4051:                                                        .getSubIntervalCount();
4052:                                        if (gapParent.getSubIntervalCount() == 1) {
4053:                                            LayoutInterval last = layoutModel
4054:                                                    .removeInterval(gapParent,
4055:                                                            0);
4056:                                            operations
4057:                                                    .addContent(
4058:                                                            last,
4059:                                                            gapParent
4060:                                                                    .getParent(),
4061:                                                            layoutModel
4062:                                                                    .removeInterval(gapParent));
4063:                                        } else if (LayoutInterval
4064:                                                .canResize(resGap)
4065:                                                && !LayoutInterval
4066:                                                        .wantResize(root)) {
4067:                                            // don't lose resizability of the layout
4068:                                            index = index == 0 ? gapParent
4069:                                                    .getSubIntervalCount() - 1
4070:                                                    : 0;
4071:                                            LayoutInterval otherGap = gapParent
4072:                                                    .getSubInterval(index);
4073:                                            if (otherGap.isEmptySpace()) { // the gap should be resizing
4074:                                                layoutModel
4075:                                                        .setIntervalSize(
4076:                                                                otherGap,
4077:                                                                NOT_EXPLICITLY_DEFINED,
4078:                                                                otherGap
4079:                                                                        .getPreferredSize(),
4080:                                                                Short.MAX_VALUE);
4081:                                            }
4082:                                        }
4083:                                    } else { // set gap size
4084:                                        operations.resizeInterval(resGap, size);
4085:                                        if (size == NOT_EXPLICITLY_DEFINED
4086:                                                && LayoutInterval
4087:                                                        .canResize(resGap)) {
4088:                                            // hack: eliminate unnecessary resizing of default padding gap
4089:                                            resGap
4090:                                                    .setMaximumSize(USE_PREFERRED_SIZE);
4091:                                            boolean layoutResizing = LayoutInterval
4092:                                                    .wantResize(root);
4093:                                            resGap
4094:                                                    .setMaximumSize(Short.MAX_VALUE);
4095:                                            if (layoutResizing) { // the gap should be fixed
4096:                                                layoutModel.setIntervalSize(
4097:                                                        resGap,
4098:                                                        NOT_EXPLICITLY_DEFINED,
4099:                                                        NOT_EXPLICITLY_DEFINED,
4100:                                                        USE_PREFERRED_SIZE);
4101:                                            }
4102:                                        }
4103:                                    }
4104:                                } else if (!LayoutInterval.wantResize(root)) {
4105:                                    // no resizing gap in fixed layout of resizing container
4106:                                    int minLayoutSize = computeMinimumDesignSize(root);
4107:                                    int growth = root.getCurrentSpace().size(i)
4108:                                            - minLayoutSize;
4109:                                    if (growth > 0) { // add new resizing gap at the end to hold the new extra space
4110:                                        LayoutInterval endGap = new LayoutInterval(
4111:                                                SINGLE);
4112:                                        endGap.setSizes(NOT_EXPLICITLY_DEFINED,
4113:                                                growth, Short.MAX_VALUE);
4114:                                        operations.insertGap(endGap, root,
4115:                                                minLayoutSize, i, TRAILING);
4116:                                    }
4117:                                }
4118:                            }
4119:                        }
4120:                        if (!empty) {
4121:                            updateLayoutStructure(root, i, true);
4122:                        }
4123:                    }
4124:
4125:                    if (component.getParent() != null) {
4126:                        if (minimum == null) {
4127:                            minimum = visualMapper
4128:                                    .getComponentMinimumSize(component.getId());
4129:                            preferred = visualMapper
4130:                                    .getComponentPreferredSize(component
4131:                                            .getId());
4132:                        }
4133:                        int min = i == HORIZONTAL ? minimum.width
4134:                                : minimum.height;
4135:                        boolean externalSize = (visualMapper
4136:                                .hasExplicitPreferredSize(component.getId()) && currentSize != (i == HORIZONTAL ? preferred.width
4137:                                : preferred.height))
4138:                                || currentSize < min
4139:                                || (currentSize > min && !LayoutInterval
4140:                                        .wantResize(defaultRoot));
4141:                        operations.resizeInterval(outer,
4142:                                externalSize ? currentSize
4143:                                        : NOT_EXPLICITLY_DEFINED);
4144:                    }
4145:                }
4146:
4147:                return true;
4148:            }
4149:
4150:            private void imposeCurrentComponentSize(LayoutComponent component) {
4151:                Dimension preferred = visualMapper
4152:                        .getComponentPreferredSize(component.getId());
4153:                for (int i = 0; i < DIM_COUNT; i++) {
4154:                    LayoutInterval li = component.getLayoutInterval(i);
4155:                    int defPref = li.getPreferredSize();
4156:                    if (LayoutInterval.wantResizeInLayout(li) && defPref != 0) { // == 0 subordinate component (filling)
4157:                        // resizing component with size-defining role in parent
4158:                        int current = li.getCurrentSpace().size(i);
4159:                        int pref = i == HORIZONTAL ? preferred.width
4160:                                : preferred.height;
4161:                        if (defPref == NOT_EXPLICITLY_DEFINED)
4162:                            defPref = pref;
4163:                        if (defPref != current)
4164:                            operations.resizeInterval(li,
4165:                                    current != pref ? current
4166:                                            : NOT_EXPLICITLY_DEFINED);
4167:                    }
4168:                }
4169:            }
4170:
4171:            private void imposeCurrentGapSize(LayoutInterval gap,
4172:                    int currentSize, int dimension) {
4173:                int pad = -1;
4174:                int min = gap.getMinimumSize();
4175:                int pref = gap.getPreferredSize();
4176:                if (pref == NOT_EXPLICITLY_DEFINED) {
4177:                    if (!LayoutInterval.wantResizeInLayout(gap))
4178:                        return; // don't change default gap if not resizing
4179:                    pad = LayoutUtils.getSizeOfDefaultGap(gap, visualMapper);
4180:                    pref = pad;
4181:                }
4182:                if (currentSize != pref) { // [check for canResize?]
4183:                    if (min == NOT_EXPLICITLY_DEFINED) {
4184:                        if (pad < 0) {
4185:                            pad = LayoutUtils.getSizeOfDefaultGap(gap,
4186:                                    visualMapper);
4187:                        }
4188:                        min = pad;
4189:                    } else if (min == USE_PREFERRED_SIZE) {
4190:                        min = pref;
4191:                    }
4192:                    if (currentSize < min) {
4193:                        currentSize = min;
4194:                    }
4195:                    operations.resizeInterval(gap,
4196:                            currentSize == pad ? NOT_EXPLICITLY_DEFINED
4197:                                    : currentSize);
4198:                }
4199:            }
4200:
4201:            private void propEmptyContainer(LayoutInterval root, int dimension) {
4202:                assert root.getParent() == null
4203:                        && root.getSubIntervalCount() == 0;
4204:                LayoutInterval gap = new LayoutInterval(SINGLE);
4205:                gap.setSizes(0, root.getCurrentSpace().size(dimension),
4206:                        Short.MAX_VALUE);
4207:                layoutModel.addInterval(gap, root, 0);
4208:            }
4209:
4210:            private int computeMinimumDesignSize(LayoutInterval interval) {
4211:                int size = 0;
4212:                if (interval.isSingle()) {
4213:                    int min = interval.getMinimumSize(true);
4214:                    size = min == USE_PREFERRED_SIZE ? interval
4215:                            .getPreferredSize(true) : min;
4216:                    if (size == NOT_EXPLICITLY_DEFINED) {
4217:                        if (interval.isComponent()) {
4218:                            LayoutComponent comp = interval.getComponent();
4219:                            Dimension dim = min == USE_PREFERRED_SIZE ? visualMapper
4220:                                    .getComponentPreferredSize(comp.getId())
4221:                                    : visualMapper.getComponentMinimumSize(comp
4222:                                            .getId());
4223:                            size = interval == comp
4224:                                    .getLayoutInterval(HORIZONTAL) ? dim.width
4225:                                    : dim.height;
4226:                        } else { // gap
4227:                            size = LayoutUtils.getSizeOfDefaultGap(interval,
4228:                                    visualMapper);
4229:                        }
4230:                    }
4231:                } else if (interval.isSequential()) {
4232:                    for (int i = 0, n = interval.getSubIntervalCount(); i < n; i++) {
4233:                        size += computeMinimumDesignSize(interval
4234:                                .getSubInterval(i));
4235:                    }
4236:                } else { // parallel group
4237:                    for (int i = 0, n = interval.getSubIntervalCount(); i < n; i++) {
4238:                        size = Math.max(size, computeMinimumDesignSize(interval
4239:                                .getSubInterval(i)));
4240:                    }
4241:                }
4242:                return size;
4243:            }
4244:
4245:            // -----
4246:
4247:            // recursive
4248:            private void intervalRemoved(LayoutInterval parent, int index,
4249:                    boolean primary, boolean wasResizing, int dimension) {
4250:                if (parent.isSequential()) {
4251:                    LayoutInterval leadingGap;
4252:                    LayoutInterval leadingNeighbor;
4253:                    if (index > 0) {
4254:                        LayoutInterval li = parent.getSubInterval(index - 1);
4255:                        if (li.isEmptySpace()) {
4256:                            leadingGap = li;
4257:                            layoutModel.removeInterval(li);
4258:                            index--;
4259:                            leadingNeighbor = index > 0 ? parent
4260:                                    .getSubInterval(index - 1) : null;
4261:                        } else {
4262:                            leadingGap = null;
4263:                            leadingNeighbor = li;
4264:                        }
4265:                    } else {
4266:                        leadingGap = null;
4267:                        leadingNeighbor = null;
4268:                    }
4269:
4270:                    LayoutInterval trailingGap;
4271:                    LayoutInterval trailingNeighbor;
4272:                    if (index < parent.getSubIntervalCount()) {
4273:                        LayoutInterval li = parent.getSubInterval(index);
4274:                        if (li.isEmptySpace()) {
4275:                            trailingGap = li;
4276:                            layoutModel.removeInterval(li);
4277:                            trailingNeighbor = index < parent
4278:                                    .getSubIntervalCount() ? parent
4279:                                    .getSubInterval(index) : null;
4280:                        } else {
4281:                            trailingGap = null;
4282:                            trailingNeighbor = li;
4283:                        }
4284:                    } else {
4285:                        trailingGap = null;
4286:                        trailingNeighbor = null;
4287:                    }
4288:
4289:                    if (!wasResizing
4290:                            && ((leadingGap != null && LayoutInterval
4291:                                    .canResize(leadingGap)) || (trailingGap != null && LayoutInterval
4292:                                    .canResize(trailingGap))))
4293:                        wasResizing = true;
4294:
4295:                    LayoutInterval super Parent = parent.getParent();
4296:
4297:                    // [check for last interval (count==1), if parallel superParent try to re-add the interval]
4298:                    if (parent.getSubIntervalCount() == 0) { // nothing remained
4299:                        int idx = layoutModel.removeInterval(parent);
4300:                        if (super Parent.getParent() != null) {
4301:                            intervalRemoved(super Parent, idx, false,
4302:                                    wasResizing, dimension);
4303:                        }
4304:                        //                else if (superParent.getSubIntervalCount() == 0) { // empty root group - add a filling gap
4305:                        //                    propEmptyContainer(superParent, dimension);
4306:                        //                }
4307:                    } else { // the sequence remains
4308:                        boolean restResizing = LayoutInterval
4309:                                .contentWantResize(parent);
4310:                        if (wasResizing && !restResizing) {
4311:                            if (leadingNeighbor == null
4312:                                    && parent.getAlignment() == LEADING) {
4313:                                layoutModel.setIntervalAlignment(parent,
4314:                                        TRAILING);
4315:                            }
4316:                            if (trailingNeighbor == null
4317:                                    && parent.getAlignment() == TRAILING) {
4318:                                layoutModel.setIntervalAlignment(parent,
4319:                                        LEADING);
4320:                            }
4321:                        }
4322:
4323:                        int cutSize = LayoutRegion.distance(
4324:                                (leadingNeighbor != null ? leadingNeighbor
4325:                                        : parent).getCurrentSpace(),
4326:                                (trailingNeighbor != null ? trailingNeighbor
4327:                                        : parent).getCurrentSpace(), dimension,
4328:                                leadingNeighbor != null ? TRAILING : LEADING,
4329:                                trailingNeighbor != null ? LEADING : TRAILING);
4330:
4331:                        if ((leadingNeighbor != null && trailingNeighbor != null) // inside a sequence
4332:                                || super Parent.getParent() == null // in root parallel group
4333:                                || (leadingNeighbor != null && LayoutInterval
4334:                                        .getEffectiveAlignment(leadingNeighbor,
4335:                                                TRAILING) == TRAILING)
4336:                                || (trailingNeighbor != null && LayoutInterval
4337:                                        .getEffectiveAlignment(
4338:                                                trailingNeighbor, LEADING) == LEADING)) { // create a placeholder gap
4339:                            int min, max;
4340:                            if (wasResizing && !restResizing) { // the gap should be resizing
4341:                                min = NOT_EXPLICITLY_DEFINED;
4342:                                max = Short.MAX_VALUE;
4343:                            } else {
4344:                                min = max = USE_PREFERRED_SIZE;
4345:                            }
4346:                            LayoutInterval gap = new LayoutInterval(SINGLE);
4347:                            gap.setSizes(min, cutSize, max);
4348:                            layoutModel.addInterval(gap, parent, index);
4349:                        } else { // this is an "open" end - compensate the size in the parent
4350:                            if (parent.getSubIntervalCount() == 1) {
4351:                                LayoutInterval last = layoutModel
4352:                                        .removeInterval(parent, 0);
4353:                                layoutModel.addInterval(last, super Parent,
4354:                                        layoutModel.removeInterval(parent));
4355:                                layoutModel.setIntervalAlignment(last, parent
4356:                                        .getRawAlignment());
4357:                            } else { // adjust current space of the parent sequence
4358:                                // (border interval at the open end removed)
4359:                                int l = (trailingNeighbor != null
4360:                                        && leadingNeighbor == null ? trailingNeighbor
4361:                                        : parent).getCurrentSpace().positions[dimension][LEADING];
4362:                                int t = (leadingNeighbor != null
4363:                                        && trailingNeighbor == null ? leadingNeighbor
4364:                                        : parent).getCurrentSpace().positions[dimension][TRAILING];
4365:                                parent.getCurrentSpace().set(dimension, l, t);
4366:                            }
4367:                            maintainSize(super Parent, wasResizing
4368:                                    || restResizing, dimension, parent, parent
4369:                                    .getCurrentSpace().size(dimension)
4370:                                    - cutSize);
4371:                        }
4372:
4373:                        if (wasResizing && !restResizing) {
4374:                            operations.enableGroupResizing(super Parent); // in case it was disabled
4375:                        }
4376:                    }
4377:                } else {
4378:                    if (parent.getParent() == null)
4379:                        return; // Component placed directly in the root interval
4380:                    assert parent.isParallel()
4381:                            && parent.getSubIntervalCount() > 0;
4382:
4383:                    int groupAlign = parent.getGroupAlignment();
4384:                    if (primary
4385:                            && (groupAlign == LEADING || groupAlign == TRAILING)) {
4386:                        maintainSize(parent, wasResizing, dimension, null, 0);
4387:                    }
4388:
4389:                    if (parent.getSubIntervalCount() == 1
4390:                            && parent.getParent() != null) { // last interval in parallel group
4391:                        // cancel the group and move the interval up
4392:                        LayoutInterval remaining = parent.getSubInterval(0);
4393:                        layoutModel.removeInterval(remaining);
4394:                        layoutModel.setIntervalAlignment(remaining, parent
4395:                                .getAlignment());
4396:                        if (LayoutInterval.wantResize(remaining)
4397:                                && !LayoutInterval.canResize(parent)) {
4398:                            // resizing interval in fixed group - make it fixed
4399:                            if (remaining.isGroup())
4400:                                operations.suppressGroupResizing(remaining);
4401:                            else
4402:                                layoutModel.setIntervalSize(remaining,
4403:                                        USE_PREFERRED_SIZE, remaining
4404:                                                .getPreferredSize(),
4405:                                        USE_PREFERRED_SIZE);
4406:                        }
4407:                        LayoutInterval super Parent = parent.getParent();
4408:                        int i = layoutModel.removeInterval(parent);
4409:                        operations.addContent(remaining, super Parent, i);
4410:                        if (remaining.isSequential()
4411:                                && super Parent.isSequential()) {
4412:                            // eliminate possible directly consecutive gaps
4413:                            // [this could be done by the addContent method directly]
4414:                            eliminateConsecutiveGaps(super Parent, i, dimension);
4415:                        }
4416:                        // [TODO if parallel superParent try to re-add the interval]
4417:                    } else if (wasResizing
4418:                            && !LayoutInterval.contentWantResize(parent)) {
4419:                        operations.enableGroupResizing(parent);
4420:                    }
4421:                }
4422:            }
4423:
4424:            private void eliminateConsecutiveGaps(LayoutInterval group,
4425:                    int index, int dimension) {
4426:                assert group.isSequential();
4427:                if (index > 0)
4428:                    index--;
4429:                while (index < group.getSubIntervalCount() - 1) {
4430:                    LayoutInterval current = group.getSubInterval(index);
4431:                    LayoutInterval next = group.getSubInterval(index + 1);
4432:                    if (current.isEmptySpace() && next.isEmptySpace()) {
4433:                        int la;
4434:                        LayoutRegion lr;
4435:                        if (index > 0) {
4436:                            la = TRAILING;
4437:                            lr = group.getSubInterval(index - 1)
4438:                                    .getCurrentSpace();
4439:                        } else {
4440:                            la = LEADING;
4441:                            lr = group.getCurrentSpace();
4442:                        }
4443:                        int ta;
4444:                        LayoutRegion tr;
4445:                        if (index + 2 < group.getSubIntervalCount()) {
4446:                            ta = LEADING;
4447:                            tr = group.getSubInterval(index + 2)
4448:                                    .getCurrentSpace();
4449:                        } else {
4450:                            ta = TRAILING;
4451:                            tr = group.getCurrentSpace();
4452:                        }
4453:                        operations.eatGap(current, next, LayoutRegion.distance(
4454:                                lr, tr, dimension, la, ta));
4455:                    } else
4456:                        index++;
4457:                }
4458:            }
4459:
4460:            private void maintainSize(LayoutInterval group,
4461:                    boolean wasResizing, int dimension,
4462:                    LayoutInterval excluded, int excludedSize) {
4463:                assert group.isParallel(); // [also not used for center or baseline groups]
4464:
4465:                int groupSize = group.getCurrentSpace().size(dimension);
4466:                int[] groupPos = group.getCurrentSpace().positions[dimension];
4467:
4468:                boolean leadAlign = false;
4469:                boolean trailAlign = false;
4470:                int leadCompPos = Integer.MAX_VALUE;
4471:                int trailCompPos = Integer.MIN_VALUE;
4472:                int subSize = Integer.MIN_VALUE;
4473:
4474:                Iterator it = group.getSubIntervals();
4475:                while (it.hasNext()) {
4476:                    LayoutInterval li = (LayoutInterval) it.next();
4477:                    int align = li.getAlignment();
4478:                    int l, t; // leading and trailing position of first and last component
4479:                    if (li != excluded) {
4480:                        int size = li.getCurrentSpace().size(dimension);
4481:                        if (size >= groupSize) {
4482:                            return;
4483:                        }
4484:                        if (size > subSize) {
4485:                            subSize = size;
4486:                        }
4487:                        l = LayoutUtils.getOutermostComponent(li, dimension,
4488:                                LEADING).getCurrentSpace().positions[dimension][LEADING];
4489:                        t = LayoutUtils.getOutermostComponent(li, dimension,
4490:                                TRAILING).getCurrentSpace().positions[dimension][TRAILING];
4491:                    } else {
4492:                        if (excludedSize > subSize) {
4493:                            subSize = excludedSize;
4494:                        }
4495:                        if (align == LEADING) {
4496:                            l = groupPos[LEADING];
4497:                            t = groupPos[LEADING] + excludedSize;
4498:                        } else {// TRAILING
4499:                            l = groupPos[TRAILING] - excludedSize;
4500:                            t = groupPos[TRAILING];
4501:                        }
4502:                    }
4503:                    if (l < leadCompPos)
4504:                        leadCompPos = l;
4505:                    if (t > trailCompPos)
4506:                        trailCompPos = t;
4507:
4508:                    if (align == LEADING)
4509:                        leadAlign = true;
4510:                    else
4511:                        // TRAILING
4512:                        trailAlign = true;
4513:                }
4514:
4515:                if (leadAlign && trailAlign) {
4516:                    optimizeGaps(group, dimension, false);
4517:                } else { // one open edge to compensate
4518:                    if (!LayoutInterval.canResize(group)) { // resizing disabled on the group
4519:                        wasResizing = false;
4520:                    }
4521:                    boolean resizing = LayoutInterval.wantResize(group);
4522:                    LayoutInterval parent = group.getParent();
4523:                    if (parent != null
4524:                            && parent.isParallel()
4525:                            && group.getAlignment() == (leadAlign ? LEADING
4526:                                    : TRAILING)) { // the group can shrink and the parent compensate
4527:                        maintainSize(parent, wasResizing && !resizing,
4528:                                dimension, group, subSize);
4529:                        if (leadAlign) {
4530:                            groupPos[TRAILING] = trailCompPos;
4531:                        }
4532:                        if (trailAlign) {
4533:                            groupPos[LEADING] = leadCompPos;
4534:                        }
4535:                        groupPos[CENTER] = (groupPos[LEADING] + groupPos[LEADING]) / 2;
4536:                    } else {
4537:                        int increment = groupSize - subSize;
4538:                        assert increment > 0;
4539:                        LayoutInterval gap = new LayoutInterval(SINGLE);
4540:                        int min, max;
4541:                        if (!resizing && (wasResizing || parent == null)) {
4542:                            min = NOT_EXPLICITLY_DEFINED;
4543:                            max = Short.MAX_VALUE;
4544:                        } else {
4545:                            min = max = USE_PREFERRED_SIZE;
4546:                        }
4547:                        gap.setSizes(min, increment, max);
4548:
4549:                        operations.insertGap(gap, group,
4550:                                leadAlign ? trailCompPos : leadCompPos,
4551:                                dimension, leadAlign ? TRAILING : LEADING);
4552:
4553:                        if (leadAlign) {
4554:                            groupPos[TRAILING] = trailCompPos;
4555:                        }
4556:                        if (trailAlign) {
4557:                            groupPos[LEADING] = leadCompPos;
4558:                        }
4559:                        groupPos[CENTER] = (groupPos[LEADING] + groupPos[LEADING]) / 2;
4560:
4561:                        if (parent != null) {
4562:                            if (parent.isSequential()) {
4563:                                parent = parent.getParent();
4564:                            }
4565:                            optimizeGaps(parent, dimension, false);
4566:                        }
4567:                    }
4568:                }
4569:            }
4570:
4571:            public String[] positionCode() {
4572:                return dragger != null ? dragger.positionCode() : new String[2];
4573:            }
4574:
4575:            // -----
4576:            // auxiliary fields holding temporary objects used frequently
4577:
4578:            // converted cursor position used during moving/resizing
4579:            private int[] cursorPos = { 0, 0 };
4580:
4581:            // -----
4582:            // test generation support
4583:
4584:            static final String TEST_SWITCH = "netbeans.form.layout_test"; // NOI18N
4585:
4586:            /* stores test code lines */
4587:            public List<String> testCode = new ArrayList<String>();
4588:
4589:            // these below are used for removing unwanted move entries, otherwise the code can exceed 10000 lines in a few seconds of form editor work ;O)
4590:            private List<String> testCode0 = new ArrayList<String>();
4591:            private List<String> beforeMove = new ArrayList<String>();
4592:            private List<String> move1 = new ArrayList<String>();
4593:            private List<String> move2 = new ArrayList<String>();
4594:            private boolean isMoving = false;
4595:
4596:            private int modelCounter = -1;
4597:
4598:            private Point lastMovePoint = new Point(0, 0);
4599:
4600:            public int getModelCounter() {
4601:                return modelCounter;
4602:            }
4603:
4604:            public void setModelCounter(int modelCounter) {
4605:                this .modelCounter = modelCounter;
4606:            }
4607:
4608:            public static boolean testMode() {
4609:                return Boolean.getBoolean(TEST_SWITCH);
4610:            }
4611:
4612:            public boolean logTestCode() {
4613:                return modelCounter > -1 && Boolean.getBoolean(TEST_SWITCH);
4614:            }
4615:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.