KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > form > layoutdesign > LayoutDesigner


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.form.layoutdesign;
21
22
23 import java.awt.BasicStroke JavaDoc;
24 import java.awt.Dimension JavaDoc;
25 import java.awt.Graphics2D JavaDoc;
26 import java.awt.Image JavaDoc;
27 import java.awt.Point JavaDoc;
28 import java.awt.Rectangle JavaDoc;
29 import java.awt.RenderingHints JavaDoc;
30 import java.awt.Stroke JavaDoc;
31 import java.util.*;
32 import org.openide.loaders.DataObject;
33 import org.openide.util.Utilities;
34
35 public class LayoutDesigner implements LayoutConstants {
36
37     private LayoutModel layoutModel;
38
39     private VisualMapper visualMapper;
40
41     private LayoutDragger dragger;
42
43     private LayoutOperations operations;
44
45     private Listener modelListener;
46
47     private boolean imposeSize = true;
48     private boolean optimizeStructure = true;
49     private boolean visualStateUpToDate;
50
51     // -----
52

53     public LayoutDesigner(LayoutModel model, VisualMapper mapper) {
54         layoutModel = model;
55         visualMapper = mapper;
56         operations = new LayoutOperations(model, mapper);
57         modelListener = new Listener();
58         modelListener.activate();
59     }
60
61     // -------
62
// updates of the current visual state stored in the model
63

64     public boolean updateCurrentState() {
65         if (logTestCode()) {
66             testCode.add("// > UPDATE CURRENT STATE"); //NOI18N
67
}
68         Object JavaDoc changeMark = layoutModel.getChangeMark();
69         boolean changeRequired = imposeSize || optimizeStructure;
70         Set updatedContainers = changeRequired ? new HashSet() : null;
71         try {
72             if (changeRequired) {
73                 modelListener.deactivate(); // some changes may happen...
74
destroyRedundantGroups(updatedContainers);
75                 operations.mergeAdjacentGaps(updatedContainers);
76             }
77
78             updatePositions(updatedContainers);
79         } finally {
80             if (changeRequired) {
81                 modelListener.activate();
82             }
83         }
84
85         if (changeRequired) {
86             imposeSize = optimizeStructure = false;
87
88             if (updatedContainers != null) {
89                 Iterator it = updatedContainers.iterator();
90                 while (it.hasNext()) {
91                     LayoutComponent cont = (LayoutComponent) it.next();
92                     visualMapper.rebuildLayout(cont.getId());
93                 }
94                 updatePositions(null);
95             }
96         }
97
98         if (logTestCode()) {
99             testCode.add("ld.updateCurrentState();"); //NOI18N
100
testCode.add("// < UPDATE CURRENT STATE"); //NOI18N
101
}
102         
103         visualStateUpToDate = true;
104         return !changeMark.equals(layoutModel.getChangeMark());
105     }
106
107     public void externalSizeChangeHappened() {
108         imposeSize = true;
109         visualStateUpToDate = false;
110         if (logTestCode()) {
111             testCode.add("ld.externalSizeChangeHappened();"); // NOI18N
112
}
113     }
114
115     void requireStructureOptimization() {
116         optimizeStructure = true;
117         visualStateUpToDate = false;
118
119         Iterator it = layoutModel.getAllComponents();
120         while (it.hasNext()) {
121             LayoutComponent comp = (LayoutComponent) it.next();
122             if (comp.isLayoutContainer()) {
123                 for (int i=0; i < DIM_COUNT; i++) {
124                     cleanDesignAttrs(comp.getLayoutRoot(i));
125                 }
126             }
127         }
128     }
129
130     private void updatePositions(Set updatedContainers) {
131         Iterator it = layoutModel.getAllComponents();
132         while (it.hasNext()) {
133             LayoutComponent comp = (LayoutComponent) it.next();
134             if (!comp.isLayoutContainer())
135                 continue;
136
137             if (optimizeStructure || imposeSize) {
138                 // make sure the layout definition reflects the current size
139
if (imposeCurrentContainerSize(comp, null, false)) {
140                     updatedContainers.add(comp);
141                     for (int i=0; i < DIM_COUNT; i++) {
142                         LayoutInterval root = comp.getLayoutRoot(i);
143                         if (optimizeStructure) {
144                             optimizeGaps(root, i, true);
145                             destroyRedundantGroups(root);
146                         }
147                         updateDesignModifications(root, i);
148                     }
149                 }
150             }
151             else { // just update the current visual positions (LayoutRegion)
152
Rectangle JavaDoc bounds = visualMapper.getContainerInterior(comp.getId());
153                 if (bounds != null) {
154                     comp.setCurrentInterior(bounds);
155                     Iterator it2 = comp.getSubcomponents();
156                     while (it2.hasNext()) {
157                         LayoutComponent subComp = (LayoutComponent) it2.next();
158                         bounds = visualMapper.getComponentBounds(subComp.getId());
159                         int baseline = visualMapper.getBaselinePosition(subComp.getId(), bounds.width, bounds.height);
160                         subComp.setCurrentBounds(bounds, baseline);
161                     }
162                     for (int i=0; i < DIM_COUNT; i++) {
163                         updateLayoutStructure(comp.getLayoutRoot(i), i, false);
164                     }
165                 }
166             }
167         }
168     }
169
170     // recursive
171
/**
172      * Updates current space (LayoutRegion) of all groups in the layout.
173      * Also for all intervals having the preferred size explicitly defined the
174      * pref size is updated according to the current state. Min and max sizes
175      * as well if they are the same as pref.
176      */

177     void updateLayoutStructure(LayoutInterval interval, int dimension, boolean imposeGaps) {
178         LayoutRegion space = interval.getCurrentSpace();
179         boolean baseline = interval.getGroupAlignment() == BASELINE;
180
181         boolean first = true;
182         boolean firstResizingSpace = false;
183         int leadingSpace = 0;
184         boolean skipNext = false;
185
186         Iterator it = interval.getSubIntervals();
187         while (it.hasNext()) {
188             LayoutInterval sub = (LayoutInterval) it.next();
189             if (sub.isEmptySpace()) {
190                 if (!interval.isSequential()) {
191                     // filling gap in empty root group
192
assert interval.getParent() == null;
193                     if (imposeGaps && space.isSet(dimension)) {
194                         imposeCurrentGapSize(sub, space.size(dimension), dimension);
195                     }
196                 }
197                 else if (first || !it.hasNext()) {
198                     // first or last gap in sequence
199
int min = sub.getMinimumSize(true);
200                     int pref = sub.getPreferredSize(true);
201                     int max = sub.getMaximumSize(true);
202                     if ((min == pref || min == USE_PREFERRED_SIZE)
203                         && (pref == max || max == USE_PREFERRED_SIZE)
204                         && pref != NOT_EXPLICITLY_DEFINED) {
205                         // Fixed size gap
206
if (first) {
207                             leadingSpace = pref;
208                         } else {
209                             space.reshape(dimension, TRAILING, pref);
210                         }
211                     } else {
212                         int currentPref = LayoutRegion.UNKNOWN;
213                         LayoutInterval sibling = LayoutInterval.getNeighbor(sub, SEQUENTIAL, first ? LEADING : TRAILING);
214                         if (sibling == null) {
215                             LayoutRegion rootSpace = LayoutInterval.getRoot(interval).getCurrentSpace();
216                             if (first) {
217                                 firstResizingSpace = true;
218                                 leadingSpace = - rootSpace.positions[dimension][LEADING];
219                             } else { // last
220
currentPref = rootSpace.positions[dimension][TRAILING] - space.positions[dimension][TRAILING];
221                                 space.reshape(dimension, TRAILING, currentPref);
222                             }
223                         } else if (sibling.isEmptySpace()) {
224                             LayoutInterval parent = interval.getParent();
225                             LayoutInterval alignedParent = parent;
226                             int align = first ? LEADING : TRAILING;
227                             while (parent != null && LayoutInterval.isAlignedAtBorder(interval, parent, align)) {
228                                 alignedParent = parent;
229                                 parent = parent.getParent();
230                             }
231                             LayoutInterval parComp = LayoutUtils.getOutermostComponent(alignedParent, dimension, align);
232                             // [this assert is maybe too strong - it's ok if the component at least touches the border - but that's hard to determine here]
233
// assert LayoutInterval.isAlignedAtBorder(parComp, interval.getParent(), first ? LEADING : TRAILING);
234
LayoutRegion parSpace = parComp.getCurrentSpace();
235                             if (first) {
236                                 firstResizingSpace = true;
237                                 leadingSpace = - parSpace.positions[dimension][LEADING];
238                             }
239                             else {
240                                 currentPref = parSpace.positions[dimension][TRAILING] - space.positions[dimension][TRAILING];
241                                 space.reshape(dimension, TRAILING, currentPref);
242                             }
243                         } else {
244                             if (first) {
245                                 firstResizingSpace = true;
246                                 LayoutRegion sibSpace = sibling.getCurrentSpace();
247                                 leadingSpace = - sibSpace.positions[dimension][TRAILING];
248                             } else {
249                                 LayoutRegion sibSpace = sibling.getCurrentSpace();
250                                 if (!sibling.isComponent()) {
251                                     sibSpace.reset();
252                                     updateLayoutStructure(sibling, dimension, imposeGaps);
253                                     skipNext = true;
254                                 }
255                                 int sibPos = sibSpace.positions[dimension][LEADING];
256                                 if (sibPos != LayoutRegion.UNKNOWN) { // Issue 62320
257
currentPref = sibPos - space.positions[dimension][TRAILING];
258                                     space.reshape(dimension, TRAILING, currentPref);
259                                 }
260                             }
261                         }
262
263                         if (imposeGaps && (currentPref != LayoutRegion.UNKNOWN)) { // last resizing gap
264
imposeCurrentGapSize(sub, currentPref, dimension);
265                         }
266                     }
267                 }
268                 else if (imposeGaps) {
269                     LayoutInterval sibling = LayoutInterval.getDirectNeighbor(sub, TRAILING, false);
270                     assert !sibling.isEmptySpace();
271                     LayoutRegion sibSpace = sibling.getCurrentSpace();
272                     if (!sibling.isComponent()) {
273                         sibSpace.reset();
274                         updateLayoutStructure(sibling, dimension, imposeGaps);
275                         skipNext = true;
276                     }
277                     int currentSize = LayoutRegion.distance(space, sibSpace, dimension, TRAILING, LEADING);
278                     imposeCurrentGapSize(sub, currentSize, dimension);
279                 }
280                 first = false;
281                 continue;
282             }
283
284             LayoutRegion subSpace = sub.getCurrentSpace();
285             if (skipNext) { // this subgroup has been processed in advance
286
skipNext = false;
287             }
288             else if (sub.isGroup()) {
289                 assert sub.getSubIntervalCount() > 0; // consistency check - there should be no empty groups
290
subSpace.reset();
291                     /*if (interval.getParent() == null) {
292                         subSpace.set(space, dimension); // root space is known
293                     }*/

294                 updateLayoutStructure(sub, dimension, imposeGaps);
295             }
296             space.expand(subSpace);
297
298             if (baseline && sub.isComponent()) {
299                 int baselinePos = subSpace.positions[dimension][BASELINE];
300                 if (baselinePos != LayoutRegion.UNKNOWN) {
301                     space.positions[dimension][BASELINE] = baselinePos;
302                     baseline = false;
303                 }
304             }
305             if (firstResizingSpace) {
306                 leadingSpace += space.positions[dimension][LEADING];
307                 firstResizingSpace = false;
308                 if (imposeGaps) {
309                     imposeCurrentGapSize(interval.getSubInterval(0), leadingSpace, dimension);
310                 }
311             }
312             first = false;
313         }
314         if (leadingSpace != 0) {
315             space.reshape(dimension, LEADING, -leadingSpace);
316         }
317     }
318
319     public void dumpTestcode(DataObject form) {
320         LayoutTestUtils.dumpTestcode(testCode, form, getModelCounter());
321         testCode = new ArrayList();
322         testCode0 = new ArrayList();
323         beforeMove = new ArrayList();
324         move1 = new ArrayList();
325         move2 = new ArrayList();
326         isMoving = false;
327     }
328     
329     // -----
330
// adding, moving, resizing
331

332     public void startAdding(LayoutComponent[] comps,
333                             Rectangle JavaDoc[] bounds,
334                             Point JavaDoc hotspot,
335                             String JavaDoc defaultContId)
336     {
337         if (logTestCode()) {
338             testCode.add("// > START ADDING"); //NOI18N
339
}
340         prepareDragger(comps, bounds, hotspot, LayoutDragger.ALL_EDGES);
341         if (logTestCode()) {
342             testCode.add("{"); // NOI18N
343
// lc should be already filled in the MetaComponentCreator.getPrecreatedComponent
344
LayoutTestUtils.writeLayoutComponentArray(testCode, "comps", "lc"); //NOI18N
345
LayoutTestUtils.writeRectangleArray(testCode, "bounds", bounds); //NOI18N
346
LayoutTestUtils.writeString(testCode, "defaultContId", defaultContId); //NOI18N
347
testCode.add("Point hotspot = new Point(" + new Double JavaDoc(hotspot.getX()).intValue() + "," + //NOI18N
348
new Double JavaDoc(hotspot.getY()).intValue() + ");"); //NOI18N
349
testCode.add("ld.startAdding(comps, bounds, hotspot, defaultContId);"); //NOI18N
350
testCode.add("}"); //NOI18N
351
}
352         if (defaultContId != null)
353             dragger.setTargetContainer(layoutModel.getLayoutComponent(defaultContId));
354         if (logTestCode()) {
355             testCode.add("// < START ADDING"); //NOI18N
356
}
357     }
358     
359     public void startMoving(String JavaDoc[] compIds, Rectangle JavaDoc[] bounds, Point JavaDoc hotspot) {
360         if (logTestCode()) {
361             testCode.add("// > START MOVING"); //NOI18N
362
}
363
364         LayoutComponent[] comps = new LayoutComponent[compIds.length];
365         for (int i=0; i < compIds.length; i++) {
366             comps[i] = layoutModel.getLayoutComponent(compIds[i]);
367         }
368         prepareDragger(comps, bounds, hotspot, LayoutDragger.ALL_EDGES);
369
370         if (logTestCode()) {
371             testCode.add("{"); //NOI18N
372
LayoutTestUtils.writeStringArray(testCode, "compIds", compIds); //NOI18N
373
LayoutTestUtils.writeRectangleArray(testCode, "bounds", bounds); //NOI18N
374
testCode.add("Point hotspot = new Point(" + new Double JavaDoc(hotspot.getX()).intValue() + "," + //NOI18N
375
new Double JavaDoc(hotspot.getY()).intValue() + ");"); //NOI18N
376
testCode.add("ld.startMoving(compIds, bounds, hotspot);"); //NOI18N
377
testCode.add("}"); //NOI18N
378
}
379         
380         dragger.setTargetContainer(comps[0].getParent());
381
382         if (logTestCode()) {
383             testCode.add("// < START MOVING"); //NOI18N
384
}
385     }
386
387     // [change to one component only?]
388
public void startResizing(String JavaDoc[] compIds,
389                               Rectangle JavaDoc[] bounds,
390                               Point JavaDoc hotspot,
391                               int[] resizeEdges,
392                               boolean inLayout)
393     {
394         if (logTestCode()) {
395             testCode.add("// > START RESIZING"); //NOI18N
396
}
397
398         LayoutComponent[] comps = new LayoutComponent[compIds.length];
399         for (int i=0; i < compIds.length; i++) {
400             comps[i] = layoutModel.getLayoutComponent(compIds[i]);
401         }
402         
403         int[] edges = new int[DIM_COUNT];
404         for (int i=0; i < DIM_COUNT; i++) {
405             edges[i] = resizeEdges[i] == LEADING || resizeEdges[i] == TRAILING ?
406                        resizeEdges[i] : LayoutRegion.NO_POINT;
407         }
408
409         prepareDragger(comps, bounds, hotspot, edges);
410
411         if (logTestCode()) {
412             testCode.add("{"); //NOI18N
413
LayoutTestUtils.writeStringArray(testCode, "compIds", compIds); //NOI18N
414
LayoutTestUtils.writeRectangleArray(testCode, "bounds", bounds); //NOI18N
415
testCode.add("Point hotspot = new Point(" + new Double JavaDoc(hotspot.getX()).intValue() + "," + //NOI18N
416
new Double JavaDoc(hotspot.getY()).intValue() + ");"); //NOI18N
417
LayoutTestUtils.writeIntArray(testCode, "resizeEdges", resizeEdges); //NOI18N
418
testCode.add("boolean inLayout = " + inLayout + ";"); // NOI18N
419
testCode.add("ld.startResizing(compIds, bounds, hotspot, resizeEdges, inLayout);"); //NOI18N
420
testCode.add("}"); //NOI18N
421
}
422
423         dragger.setTargetContainer(inLayout ? comps[0].getParent() : null);
424
425         if (logTestCode()) {
426             testCode.add("// < START RESIZING"); //NOI18N
427
}
428     }
429
430     private void prepareDragger(LayoutComponent[] comps,
431                                 Rectangle JavaDoc[] bounds,
432                                 Point JavaDoc hotspot,
433                                 int[] edges)
434     {
435         if (comps.length != bounds.length)
436             throw new IllegalArgumentException JavaDoc();
437
438         LayoutRegion[] movingFormation = new LayoutRegion[bounds.length];
439         for (int i=0; i < bounds.length; i++) {
440             int baseline = visualMapper.getBaselinePosition(comps[i].getId(), bounds[i].width, bounds[i].height);
441             int baselinePos = baseline > 0 ? bounds[i].y + baseline : LayoutRegion.UNKNOWN;
442             movingFormation[i] = new LayoutRegion();
443             movingFormation[i].set(bounds[i], baselinePos);
444         }
445
446         dragger = new LayoutDragger(comps,
447                                     movingFormation,
448                                     new int[] { hotspot.x, hotspot.y },
449                                     edges,
450                                     visualMapper);
451     }
452
453     /**
454      * @param p mouse cursor position in coordinates of the whole design area;
455      * it is adjusted if the position is changed (due to a snap effect)
456      * @param containerId for container the cursor is currently moved over,
457      * can be null if e.g. a root container is resized
458      * @param autoPositioning if true, searching for optimal position will be
459      * performed - a found position will be painted and the moving
460      * component snapped to it
461      * @param lockDimension if true, one dimension is locked for this move
462      * (does not change); the dimension to lock must have aligned
463      * position found in previous move steps - if this is true for both
464      * dimensions then the one with smaller delta is chosen
465      * @param bounds (output) bounds of moving components after the move
466      */

467     public void move(Point JavaDoc p,
468                      String JavaDoc containerId,
469                      boolean autoPositioning,
470                      boolean lockDimension,
471                      Rectangle JavaDoc[] bounds)
472     {
473     
474     int x = (p != null) ? p.x : 0;
475     int y = (p != null) ? p.y : 0;
476
477     if (logTestCode()) {
478             // this terrible code here is to store only two last move() calls
479
if (!isMoving) {
480                 isMoving = true;
481                 // backup all current entries and clear the testcode list
482
beforeMove = new ArrayList();
483                 beforeMove.addAll(testCode);
484                 testCode = new ArrayList();
485         lastMovePoint = new Point JavaDoc(0,0);
486             }
487
488         if (!((x == lastMovePoint.x) && (y == lastMovePoint.y))) {
489         lastMovePoint = new Point JavaDoc(x, y);
490                 move1 = move2;
491         testCode0 = testCode;
492         }
493
494             move2 = new ArrayList();
495             move2.add("// > MOVE");
496             testCode = new ArrayList();
497         }
498         if ((!visualStateUpToDate) || (dragger == null)) {
499             return; // visual state of layout structure not updated yet (from last operation)
500
}
501
502         if (!dragger.isResizing() && (!lockDimension || dragger.getTargetContainer() == null)) {
503             dragger.setTargetContainer(layoutModel.getLayoutComponent(containerId));
504         }
505
506         cursorPos[HORIZONTAL] = p.x;
507         cursorPos[VERTICAL] = p.y;
508
509         dragger.move(cursorPos, autoPositioning, lockDimension);
510         
511         p.x = cursorPos[HORIZONTAL];
512         p.y = cursorPos[VERTICAL];
513
514         if (bounds != null) {
515             LayoutRegion[] current = dragger.getMovingBounds();
516             for (int i=0; i < current.length; i++) {
517                 current[i].toRectangle(bounds[i]);
518             }
519         }
520
521         if (logTestCode()) {
522             move2.add("{"); //NOI18N
523
move2.add("Point p = new Point(" + x + "," + y + ");"); //NOI18N
524
LayoutTestUtils.writeString(move2, "containerId", containerId); //NOI18N
525
move2.add("boolean autoPositioning = " + autoPositioning + ";"); //NOI18N
526
move2.add("boolean lockDimension = " + lockDimension + ";"); //NOI18N
527
LayoutTestUtils.writeRectangleArray(move2, "bounds", bounds); //NOI18N
528
move2.add("ld.move(p, containerId, autoPositioning, lockDimension, bounds);"); //NOI18N
529
move2.add("}"); //NOI18N
530
move2.add("// < MOVE"); //NOI18N
531
}
532     }
533
534     public void endMoving(boolean committed) {
535         if (!committed && dragger == null)
536             return; // redundant call
537

538         if (logTestCode()) {
539             if (committed) {
540                 beforeMove.addAll(testCode0);
541                 beforeMove.addAll(move1);
542                 beforeMove.addAll(testCode);
543                 beforeMove.addAll(move2);
544                 testCode = beforeMove;
545             }
546             testCode.add("// > END MOVING"); //NOI18N
547
isMoving = false;
548         }
549         try {
550             if (committed) {
551                 LayoutComponent[] components = dragger.getMovingComponents();
552                 LayoutComponent targetContainer = dragger.getTargetContainer();
553
554                 if (targetContainer != null) {
555                     boolean newComponent = components[0].getParent() == null;
556                     // Determine intervals that should be added
557
LayoutInterval[] addingInts = new LayoutInterval[DIM_COUNT];
558                     LayoutRegion origSpace = null;
559                     for (int dim=0; dim < DIM_COUNT; dim++) {
560                         if (components.length > 1) {
561                             if (newComponent) {
562                                 // Moving several components from old layout
563
for (int i=0; i<components.length; i++) {
564                                     layoutModel.addComponent(components[i], targetContainer, -1);
565                                 }
566                                 addingInts = layoutModel.createIntervalsFromBounds(dragger.getMovingSpace(), components, dragger.getMovingBounds());
567                                 break;
568                             } else {
569                                 if (origSpace == null) {
570                                     origSpace = new LayoutRegion();
571                                     // Calculate original space
572
for (int i=0; i < components.length; i++) {
573                                         origSpace.expand(components[i].getLayoutInterval(0).getCurrentSpace());
574                                     }
575                                 }
576                                 LayoutInterval[] children = new LayoutInterval[components.length];
577                                 for (int i=0; i<components.length; i++) {
578                                     children[i] = components[i].getLayoutInterval(dim);
579                                 }
580                                 LayoutInterval parent = LayoutInterval.getCommonParent(children);
581                                 // Restriction of the layout model of the common parent
582
// in the original layout (this also removes the original intervals)
583
addingInts[dim] = restrictedCopy(parent, components, origSpace, dim, null);
584                             }
585                         } else {
586                             addingInts[dim] = components[0].getLayoutInterval(dim);
587                             if (newComponent) { // Ensure correct size when the component comes from old layout
588
Dimension JavaDoc preferred = visualMapper.getComponentPreferredSize(components[0].getId());
589                                 int size = dragger.getMovingBounds()[0].size(dim);
590                                 if (size != ((dim == HORIZONTAL) ? preferred.width : preferred.height)) {
591                                     LayoutInterval intr = addingInts[dim];
592                                     layoutModel.setIntervalSize(intr, intr.getMinimumSize(), size, intr.getMaximumSize());
593                                 }
594                             }
595                         }
596                     }
597
598                     LayoutFeeder layoutFeeder = new LayoutFeeder(operations, dragger, addingInts);
599
600                     // Remove components from original location
601
for (int i=0; i<components.length; i++) {
602                         if (components[i].getParent() != null) {
603                             LayoutComponent comp = components[i];
604                             if (components[i].getParent() != null) {
605                                 if (dragger.isResizing(HORIZONTAL)) {
606                                     layoutModel.removeComponentFromLinkSizedGroup(components[i], HORIZONTAL);
607                                 }
608                                 if (dragger.isResizing(VERTICAL)) {
609                                     layoutModel.removeComponentFromLinkSizedGroup(components[i], VERTICAL);
610                                 }
611                             }
612                             if (components.length == 1) { // remove also the intervals
613
layoutModel.removeComponentAndIntervals(comp, false);
614                             }
615                             else { // Don't remove layout intervals of components if
616
// moving multiple components - the intervals are placed
617
// in the adding group already (by restrictedCopy)
618
layoutModel.removeComponent(comp, false);
619                             }
620                         }
621                     }
622
623                     modelListener.deactivate(); // from now do not react on model changes
624

625                     // Add components to the target container
626
for (int i=0; i<components.length; i++) {
627                         layoutModel.addComponent(components[i], targetContainer, -1);
628                     }
629
630                     // add the intervals
631
layoutFeeder.add();
632                     if (layoutFeeder.imposeSize)
633                         imposeSize = true;
634                     if (layoutFeeder.optimizeStructure)
635                         optimizeStructure = true;
636                     // if an overlap occurred we can't calculate the correct sizes
637
// of resizing intervals, thus need to do real layout first (to
638
// get the right picture), then update the actual sizes, and then
639
// re-layout with design specific attributes (container resizing gap)
640

641                     for (int dim=0; dim < DIM_COUNT; dim++) {
642                         destroyGroupIfRedundant(addingInts[dim], addingInts[dim].getParent());
643                     }
644
645                     if (components[0].isLayoutContainer() && (dragger.isResizing() || newComponent)) {
646                         // container size needs to be defined from inside in advance
647
imposeCurrentContainerSize(components[0], dragger.getSizes(), true);
648                     }
649                     else if (dragger.isResizing() && !components[0].isLayoutContainer()) {
650                         // component might be resized to default size
651
for (int i=0; i < DIM_COUNT; i++) {
652                             if (dragger.snappedToDefaultSize(i))
653                                operations.resizeInterval(components[0].getLayoutInterval(i), NOT_EXPLICITLY_DEFINED);
654                         }
655                     }
656
657                     updateDesignModifications(targetContainer);
658                 }
659                 else { // resizing root container
660
assert dragger.isResizing();
661
662                     modelListener.deactivate(); // do not react on model changes
663

664                     LayoutRegion space = dragger.getMovingBounds()[0];
665                     for (int dim=0; dim < DIM_COUNT; dim++) {
666                         components[0].getLayoutInterval(dim).setCurrentSpace(space);
667                     }
668                     if (components[0].isLayoutContainer()) {
669                         imposeCurrentContainerSize(components[0], dragger.getSizes(), true);
670                     }
671                 }
672
673                 if (dragger.isResizing() && components[0].isLayoutContainer())
674                     updateDesignModifications(components[0]);
675
676                 visualStateUpToDate = false;
677             }
678         } finally {
679             modelListener.activate();
680             dragger = null;
681             if (logTestCode()) {
682                 testCode.add("ld.endMoving(" + committed + ");"); //NOI18N
683
testCode.add("// < END MOVING"); //NOI18N
684
}
685         }
686     }
687
688     /**
689      * Creates copy of the given interval restricted to specified components
690      * and region (<code>space</code>).
691      *
692      * @param interval interval whose restricted copy should be created.
693      * @param components components that determine the restriction.
694      * @param space region (current space) that determine the restriction.
695      * @param dimension dimension that restricted layout interval belongs to.
696      * @param temp internal helper parameter for recursive invocation.
697      * Pass <code>null</code> when you invoke this method.
698      */

699     private LayoutInterval restrictedCopy(LayoutInterval interval,
700         LayoutComponent[] components, LayoutRegion space, int dimension, List temp) {
701         boolean processTemp = (temp == null);
702         if (temp == null) {
703             temp = new LinkedList();
704         }
705         if (interval.isGroup()) {
706             boolean parallel = interval.isParallel();
707             LayoutInterval copy = new LayoutInterval(parallel ? PARALLEL : SEQUENTIAL);
708             copy.setAlignment(interval.getAlignment());
709             copy.setAttributes(interval.getAttributes());
710             copy.setSizes(interval.getMinimumSize(), interval.getPreferredSize(), interval.getMaximumSize());
711             if (parallel) {
712                 copy.setGroupAlignment(interval.getGroupAlignment());
713             }
714             Iterator iter = interval.getSubIntervals();
715             int compCount = 0; // Number of components already copied to the group
716
boolean includeGap = false; // Helper variables that allow us to insert gaps
717
int firstGapToInclude = 0; // instead of components that has been filtered out.
718
int gapStart = interval.getCurrentSpace().positions[dimension][LEADING];
719             while (iter.hasNext()) {
720                 LayoutInterval sub = (LayoutInterval)iter.next();
721                 LayoutInterval subCopy = restrictedCopy(sub, components, space, dimension, temp);
722                 if (subCopy != null) {
723                     if (!sub.isEmptySpace()) {
724                         if (includeGap) {
725                             gapStart = Math.max(space.positions[dimension][LEADING], gapStart);
726                             int size = sub.getCurrentSpace().positions[dimension][LEADING] - gapStart;
727                             integrateGap(copy, size, firstGapToInclude);
728                             includeGap = false;
729                         }
730                         gapStart = sub.getCurrentSpace().positions[dimension][TRAILING];
731                         firstGapToInclude = copy.getSubIntervalCount();
732                     }
733                     if (sub.isComponent()) {
734                         // Remember where to add component intervals - they cannot
735
// be moved immediately because the model listener would
736
// destroy the adjacent intervals before we would be able
737
// to copy them.
738
temp.add(subCopy);
739                         temp.add(copy);
740                         temp.add(new Integer JavaDoc(subCopy.getRawAlignment()));
741                         temp.add(new Integer JavaDoc(copy.getSubIntervalCount() + compCount));
742                         compCount++;
743                     } else {
744                         layoutModel.addInterval(subCopy, copy, -1);
745                     }
746                 } else {
747                     if (!parallel) {
748                         includeGap = true;
749                     }
750                 }
751             }
752             if (includeGap) {
753                 gapStart = Math.max(space.positions[dimension][LEADING], gapStart);
754                 int gapEnd = Math.min(space.positions[dimension][TRAILING], interval.getCurrentSpace().positions[dimension][TRAILING]);
755                 integrateGap(copy, gapEnd - gapStart, firstGapToInclude);
756             }
757             if (copy.getSubIntervalCount() + compCount > 0) {
758                 if (processTemp) {
759                     // Insert component intervals
760
iter = temp.iterator();
761                     while (iter.hasNext()) {
762                         LayoutInterval comp = (LayoutInterval)iter.next();
763                         LayoutInterval parent = (LayoutInterval)iter.next();
764                         int alignment = ((Integer JavaDoc)iter.next()).intValue();
765                         int index = ((Integer JavaDoc)iter.next()).intValue();
766                         layoutModel.removeInterval(comp);
767                         layoutModel.setIntervalAlignment(comp, alignment);
768                         layoutModel.addInterval(comp, parent, index);
769                     }
770                     // Consolidate the groups where the components has been added
771
boolean active = modelListener.isActive();
772                     if (active) modelListener.deactivate();
773                     iter = temp.iterator();
774                     while (iter.hasNext()) {
775                         iter.next(); // skip the component
776
LayoutInterval group = (LayoutInterval)iter.next();
777                         iter.next(); iter.next(); // skip alignment and index
778
while (group.getSubIntervalCount() == 1) {
779                             LayoutInterval sub = group.getSubInterval(0);
780                             LayoutInterval parent = group.getParent();
781                             layoutModel.removeInterval(sub);
782                             int alignment = group.getAlignment();
783                             int index = layoutModel.removeInterval(group);
784                             layoutModel.setIntervalAlignment(sub, alignment);
785                             layoutModel.addInterval(sub, parent, index);
786                             group = sub;
787                         }
788                     }
789                     compCount = 0;
790                     if (active) modelListener.activate();
791                 }
792                 // consolidate copy
793
if ((copy.getSubIntervalCount() == 1) && (compCount == 0)) {
794                     boolean active = modelListener.isActive();
795                     if (active) modelListener.deactivate();
796                     LayoutInterval subCopy = copy.getSubInterval(0);
797                     layoutModel.removeInterval(subCopy);
798                     layoutModel.setIntervalAlignment(subCopy, copy.getAlignment());
799                     if (copy.isSequential() && subCopy.isEmptySpace()) {
800                         copy = null;
801                     } else {
802                         copy = subCopy;
803                     }
804                     if (active) modelListener.activate();
805                 }
806                 return copy;
807             } else {
808                 return null;
809             }
810         } else if (interval.isComponent()) {
811             LayoutComponent comp = interval.getComponent();
812             if (Arrays.asList(components).contains(comp)) {
813                 return interval; // Don't copy layout component's intervals
814
}
815             return null;
816         } else {
817             assert interval.isEmptySpace();
818             int[] bounds = emptySpaceBounds(interval, dimension);
819             int rangeStart = space.positions[dimension][LEADING];
820             int rangeEnd = space.positions[dimension][TRAILING];
821             if ((bounds[0] < rangeEnd) && (bounds[1] > rangeStart)) {
822                 LayoutInterval gap = new LayoutInterval(SINGLE);
823                 gap.setAttributes(interval.getAttributes());
824                 if ((bounds[0] < rangeStart) || (bounds[1] > rangeEnd)) {
825                     // Partial overlap with the provides space
826
int min = interval.getMinimumSize();
827                     if (min >= 0) min = USE_PREFERRED_SIZE;
828                     int pref = Math.min(bounds[1], rangeEnd) - Math.max(bounds[0], rangeStart);
829                     int max = interval.getMaximumSize();
830                     if (max >= 0) max = USE_PREFERRED_SIZE;
831                     gap.setSizes(min, pref, max);
832                 } else {
833                     gap.setSizes(interval.getMinimumSize(), interval.getPreferredSize(), interval.getMaximumSize());
834                 }
835                 return gap;
836             } else {
837                 // Outside the provided space
838
return null;
839             }
840         }
841     }
842
843     /**
844      * Helper method used by <code>restrictedCopy()</code> method.
845      * Replaces empty spaces at the end of the sequential group
846      * by an empty space of the specified size. Only empty spaces
847      * with index >= boundary are replaced.
848      *
849      * @param seqGroup sequential group.
850      * @param size size of the empty space that should be added.
851      * @param boundary index in the sequential group that limits
852      * the replacement of the empty spaces.
853      */

854     private void integrateGap(LayoutInterval seqGroup, int size, int boundary) {
855         while ((seqGroup.getSubIntervalCount() > boundary)
856             && seqGroup.getSubInterval(seqGroup.getSubIntervalCount()-1).isEmptySpace()) {
857             layoutModel.removeInterval(seqGroup.getSubInterval(seqGroup.getSubIntervalCount()-1));
858         }
859         if (size > 0) {
860             LayoutInterval gap = new LayoutInterval(SINGLE);
861             gap.setSize(size);
862             layoutModel.addInterval(gap, seqGroup, -1);
863         }
864     }
865     
866     /**
867      * Returns bounds (e.g. current space) of the empty space in the given dimension.
868      *
869      * @param emptySpace empty space.
870      * @param dimenion dimension.
871      * @return array whose the first item is the leading bound of the empty
872      * space and the second item is the trailing bound.
873      */

874     private int[] emptySpaceBounds(LayoutInterval emptySpace, int dimension) {
875         assert emptySpace.isEmptySpace();
876         int leading, trailing;
877         LayoutInterval parent = emptySpace.getParent();
878         int index = parent.indexOf(emptySpace);
879         if (index == 0) {
880             leading = parent.getCurrentSpace().positions[dimension][LEADING];
881         } else {
882             leading = parent.getSubInterval(index - 1).getCurrentSpace().positions[dimension][TRAILING];
883         }
884         if (index+1 == parent.getSubIntervalCount()) {
885             trailing = parent.getCurrentSpace().positions[dimension][TRAILING];
886         } else {
887             trailing = parent.getSubInterval(index + 1).getCurrentSpace().positions[dimension][LEADING];
888         }
889         return new int[] {leading, trailing};
890     }
891
892     /**
893      * Removes currently dragged components from layout model. Called when
894      * the components were dragged out of the form (or to a container not
895      * managed by this layout model).
896      */

897     public void removeDraggedComponents() {
898         if (dragger != null) {
899             LayoutComponent[] components = dragger.getMovingComponents();
900             for (int i=0; i < components.length; i++) {
901                 layoutModel.removeComponentAndIntervals(components[i], !components[i].isLayoutContainer());
902             }
903             endMoving(false);
904         }
905     }
906
907     public void paintMoveFeedback(Graphics2D JavaDoc g) {
908         if (dragger != null) { // Dragger might not be initialized yet
909
dragger.paintMoveFeedback(g);
910         }
911     }
912     
913     /**
914      * Paints layout information (alignment) for the selected component.
915      *
916      * @param g graphics object to use.
917      * @param componentId ID of selected component.
918      */

919     public void paintSelection(Graphics2D JavaDoc g, String JavaDoc componentId) {
920         LayoutComponent comp = layoutModel.getLayoutComponent(componentId);
921         if ((comp != null) && (comp.getParent() != null)) {
922             paintSelection(g, comp, HORIZONTAL);
923             paintSelection(g, comp, VERTICAL);
924         }
925     }
926     
927     /**
928      * Paints layout information (alignment) for the selected component
929      * and specified dimension.
930      *
931      * @param g graphics object to use.
932      * @param component selected layout component.
933      * @param dimension dimension whose layout should be visualized.
934      */

935     private void paintSelection(Graphics2D JavaDoc g, LayoutComponent component, int dimension) {
936         LayoutInterval interval = component.getLayoutInterval(dimension);
937         if (component.isLinkSized(HORIZONTAL) || component.isLinkSized(VERTICAL)) {
938             paintLinks(g, component);
939         }
940         // Paint baseline alignment
941
if (interval.getAlignment() == BASELINE) {
942             LayoutInterval alignedParent = interval.getParent();
943             int oppDimension = (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL;
944             LayoutRegion region = alignedParent.getCurrentSpace();
945             int x = region.positions[dimension][BASELINE];
946             int y1 = region.positions[oppDimension][LEADING];
947             int y2 = region.positions[oppDimension][TRAILING];
948             if ((y1 != LayoutRegion.UNKNOWN) && (y2 != LayoutRegion.UNKNOWN)) {
949                 if (dimension == HORIZONTAL) {
950                     g.drawLine(x, y1, x, y2);
951                 } else {
952                     g.drawLine(y1, x, y2, x);
953                 }
954             }
955         }
956         int lastAlignment = -1;
957         while (interval.getParent() != null) {
958             LayoutInterval parent = interval.getParent();
959             if (parent.getType() == SEQUENTIAL) {
960                 int alignment = LayoutInterval.getEffectiveAlignment(interval);
961                 int index = parent.indexOf(interval);
962                 int start, end;
963                 switch (alignment) {
964                     case LEADING:
965                         start = 0;
966                         end = index;
967                         lastAlignment = LEADING;
968                         break;
969                     case TRAILING:
970                         start = index + 1;
971                         end = parent.getSubIntervalCount();
972                         lastAlignment = TRAILING;
973                         break;
974                     default: switch (lastAlignment) {
975                         case LEADING: start = 0; end = index; break;
976                         case TRAILING: start = index+1; end = parent.getSubIntervalCount(); break;
977                         default: start = 0; end = parent.getSubIntervalCount(); break;
978                     }
979                 }
980                 for (int i=start; i<end; i++) {
981                     LayoutInterval candidate = parent.getSubInterval(i);
982                     if (candidate.isEmptySpace()) {
983                         paintAlignment(g, candidate, dimension, LayoutInterval.getEffectiveAlignment(candidate));
984                     }
985                 }
986             } else {
987                 int alignment = interval.getAlignment();
988                 if (!LayoutInterval.wantResizeInLayout(interval)) {
989                     lastAlignment = alignment;
990                 }
991                 paintAlignment(g, interval, dimension, lastAlignment);
992             }
993             interval = interval.getParent();
994         }
995     }
996
997     private void paintLinks(Graphics2D JavaDoc g, LayoutComponent component) {
998         
999         if ((component.isLinkSized(HORIZONTAL)) && (component.isLinkSized(VERTICAL))) {
1000            Map linkGroupsH = layoutModel.getLinkSizeGroups(HORIZONTAL);
1001            Map linkGroupsV = layoutModel.getLinkSizeGroups(VERTICAL);
1002            Integer JavaDoc linkIdH = new Integer JavaDoc(component.getLinkSizeId(HORIZONTAL));
1003            Integer JavaDoc linkIdV = new Integer JavaDoc(component.getLinkSizeId(VERTICAL));
1004            
1005            List lH = (List)linkGroupsH.get(linkIdH);
1006            List lV = (List)linkGroupsV.get(linkIdV);
1007
1008            Set merged = new HashSet();
1009            for (int i=0; i < lH.size(); i++) {
1010                merged.add(lH.get(i));
1011            }
1012            for (int i=0; i < lV.size(); i++) {
1013                merged.add(lV.get(i));
1014            }
1015
1016            Iterator mergedIt = merged.iterator();
1017            while (mergedIt.hasNext()) {
1018                String JavaDoc id = (String JavaDoc)mergedIt.next();
1019                LayoutComponent lc = layoutModel.getLayoutComponent(id);
1020                LayoutInterval interval = lc.getLayoutInterval(HORIZONTAL);
1021                LayoutRegion region = interval.getCurrentSpace();
1022                Image JavaDoc badge = null;
1023                if ((lV.contains(id)) && (lH.contains(id))) {
1024                    badge = getLinkBadge(BOTH_DIMENSIONS);
1025                } else {
1026                    if (lH.contains(lc.getId())) {
1027                        badge = getLinkBadge(HORIZONTAL);
1028                    }
1029                    if (lV.contains(lc.getId())) {
1030                        badge = getLinkBadge(VERTICAL);
1031                    }
1032                }
1033                int x = region.positions[HORIZONTAL][TRAILING] - region.size(HORIZONTAL) / 4 - (badge.getWidth(null) / 2);
1034                int y = region.positions[VERTICAL][LEADING] - (badge.getHeight(null));
1035                g.drawImage(badge, x, y, null);
1036            }
1037        } else {
1038            int dimension = (component.isLinkSized(HORIZONTAL)) ? HORIZONTAL : VERTICAL;
1039            Map map = layoutModel.getLinkSizeGroups(dimension);
1040            
1041            Integer JavaDoc linkId = new Integer JavaDoc(component.getLinkSizeId(dimension));
1042            List l = (List)map.get(linkId);
1043            Iterator mergedIt = l.iterator();
1044            
1045            while (mergedIt.hasNext()) {
1046                String JavaDoc id = (String JavaDoc)mergedIt.next();
1047                LayoutComponent lc = layoutModel.getLayoutComponent(id);
1048                LayoutInterval interval = lc.getLayoutInterval(dimension);
1049                LayoutRegion region = interval.getCurrentSpace();
1050                Image JavaDoc badge = getLinkBadge(dimension);
1051                int x = region.positions[HORIZONTAL][TRAILING] - region.size(HORIZONTAL) / 4 - (badge.getWidth(null) / 2);
1052                int y = region.positions[VERTICAL][LEADING] - (badge.getHeight(null));
1053                g.drawImage(badge, x, y, null);
1054            }
1055        }
1056    }
1057    
1058    private Image JavaDoc linkBadgeBoth = null;
1059    private Image JavaDoc linkBadgeHorizontal = null;
1060    private Image JavaDoc linkBadgeVertical = null;
1061    
1062    private static final int BOTH_DIMENSIONS = 2;
1063            
1064    private Image JavaDoc getLinkBadge(int dimension) {
1065        if (dimension == (BOTH_DIMENSIONS)) {
1066            if (linkBadgeBoth == null) {
1067                linkBadgeBoth = Utilities.loadImage("org/netbeans/modules/form/resources/sameboth.png"); //NOI18N
1068
}
1069            return linkBadgeBoth;
1070        }
1071        if (dimension == HORIZONTAL) {
1072            if (linkBadgeHorizontal == null) {
1073                linkBadgeHorizontal = Utilities.loadImage("org/netbeans/modules/form/resources/samewidth.png"); //NOI18N
1074
}
1075            return linkBadgeHorizontal;
1076        }
1077        if (dimension == VERTICAL) {
1078            if (linkBadgeVertical == null) {
1079                linkBadgeVertical = Utilities.loadImage("org/netbeans/modules/form/resources/sameheight.png"); //NOI18N
1080
}
1081            return linkBadgeVertical;
1082        }
1083        return null;
1084    }
1085    
1086    private void paintAlignment(Graphics2D JavaDoc g, LayoutInterval interval, int dimension, int alignment) {
1087        LayoutInterval parent = interval.getParent();
1088        boolean baseline = parent.isParallel() && (parent.getGroupAlignment() == BASELINE);
1089        LayoutRegion group = parent.getCurrentSpace();
1090        int opposite = (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL;
1091        int x1, x2, y;
1092        if (interval.isEmptySpace()) {
1093            int index = parent.indexOf(interval);
1094            int[] ya, yb;
1095            boolean x1group, x2group;
1096            if (index == 0) {
1097                x1 = group.positions[dimension][baseline ? BASELINE : LEADING];
1098                ya = visualIntervalPosition(parent, opposite, LEADING);
1099                x1group = LayoutInterval.getFirstParent(interval, PARALLEL).getParent() != null;
1100            } else {
1101                LayoutInterval x1int = parent.getSubInterval(index-1);
1102                if (x1int.isParallel() && (x1int.getGroupAlignment() == BASELINE)) {
1103                    x1 = x1int.getCurrentSpace().positions[dimension][BASELINE];
1104                } else {
1105                    if (x1int.isEmptySpace()) return;
1106                    x1 = x1int.getCurrentSpace().positions[dimension][TRAILING];
1107                }
1108                ya = visualIntervalPosition(x1int, opposite, TRAILING);
1109                x1group = x1int.isGroup();
1110            }
1111            if (index + 1 == parent.getSubIntervalCount()) {
1112                x2 = group.positions[dimension][baseline ? BASELINE : TRAILING];
1113                yb = visualIntervalPosition(parent, opposite, TRAILING);
1114                x2group = LayoutInterval.getFirstParent(interval, PARALLEL).getParent() != null;
1115            } else {
1116                LayoutInterval x2int = parent.getSubInterval(index+1);
1117                if (x2int.isParallel() && (x2int.getGroupAlignment() == BASELINE)) {
1118                    x2 = x2int.getCurrentSpace().positions[dimension][BASELINE];
1119                } else {
1120                    if (x2int.isEmptySpace()) return;
1121                    x2 = x2int.getCurrentSpace().positions[dimension][LEADING];
1122                }
1123                yb = visualIntervalPosition(x2int, opposite, LEADING);
1124                x2group = x2int.isGroup();
1125            }
1126            if ((x1 == LayoutRegion.UNKNOWN) || (x2 == LayoutRegion.UNKNOWN)) return;
1127            int y1 = Math.min(ya[1], yb[1]);
1128            int y2 = Math.max(ya[0], yb[0]);
1129            y = (y1 + y2)/2;
1130            if ((ya[1] < yb[0]) || (yb[1] < ya[0])) {
1131                // no intersection
1132
if (dimension == HORIZONTAL) {
1133                    g.drawLine(x1, ya[0], x1, y);
1134                    g.drawLine(x1, ya[0], x1, ya[1]);
1135                    g.drawLine(x2, yb[0], x2, y);
1136                    g.drawLine(x2, yb[0], x2, yb[1]);
1137                } else {
1138                    g.drawLine(ya[0], x1, y, x1);
1139                    g.drawLine(ya[0], x1, ya[1], x1);
1140                    g.drawLine(yb[0], x2, y, x2);
1141                    g.drawLine(yb[0], x2, yb[1], x2);
1142                }
1143            } else {
1144                if (dimension == HORIZONTAL) {
1145                    if (x1group) g.drawLine(x1, ya[0], x1, ya[1]);
1146                    if (x2group) g.drawLine(x2, yb[0], x2, yb[1]);
1147                } else {
1148                    if (x1group) g.drawLine(ya[0], x1, ya[1], x1);
1149                    if (x2group) g.drawLine(yb[0], x2, yb[1], x2);
1150                }
1151            }
1152        } else {
1153            LayoutRegion child = interval.getCurrentSpace();
1154            if ((alignment == LEADING) || (alignment == TRAILING)) {
1155                x1 = group.positions[dimension][baseline ? BASELINE : alignment];
1156                if (interval.isParallel() && (interval.getAlignment() == BASELINE)) {
1157                    x2 = child.positions[dimension][BASELINE];
1158                } else {
1159                    x2 = child.positions[dimension][alignment];
1160                }
1161            } else {
1162                return;
1163            }
1164            if ((x1 == LayoutRegion.UNKNOWN) || (x2 == LayoutRegion.UNKNOWN)) return;
1165            int[] pos = visualIntervalPosition(parent, opposite, alignment);
1166            y = (pos[0] + pos[1])/2;
1167            int xa = group.positions[dimension][LEADING];
1168            int xb = group.positions[dimension][TRAILING];
1169            if (parent.getParent() != null) {
1170                if (dimension == HORIZONTAL) {
1171                    if (alignment == LEADING) {
1172                        g.drawLine(xa, pos[0], xa, pos[1]);
1173                    } else if (alignment == TRAILING) {
1174                        g.drawLine(xb, pos[0], xb, pos[1]);
1175                    }
1176                } else {
1177                    if (alignment == LEADING) {
1178                        g.drawLine(pos[0], xa, pos[1], xa);
1179                    } else if (alignment == TRAILING) {
1180                        g.drawLine(pos[0], xb, pos[1], xb);
1181                    }
1182                }
1183            }
1184        }
1185        // Avoid overload of EQ when current space is incorrectly calculated.
1186
if ((x2 - x1 > 1) && (Math.abs(y) <= Short.MAX_VALUE)
1187            && (Math.abs(x1) <= Short.MAX_VALUE) && (Math.abs(x2) <= Short.MAX_VALUE)) {
1188            int x, angle;
1189            if (alignment == LEADING) {
1190                x = x1;
1191                angle = 180;
1192            } else {
1193                x = x2;
1194                angle = 0;
1195            }
1196            x2--;
1197            int diam = Math.min(4, x2-x1);
1198            Stroke JavaDoc stroke = new BasicStroke JavaDoc(1, BasicStroke.CAP_BUTT,
1199                BasicStroke.JOIN_BEVEL, 0, new float[] {1, 1}, 0);
1200            Stroke JavaDoc oldStroke = g.getStroke();
1201            g.setStroke(stroke);
1202            if (dimension == HORIZONTAL) {
1203                g.drawLine(x1, y, x2, y);
1204                angle += 90;
1205            } else {
1206                g.drawLine(y, x1, y, x2);
1207                int temp = x; x = y; y = temp;
1208            }
1209            g.setStroke(oldStroke);
1210            if ((alignment == LEADING) || (alignment == TRAILING)) {
1211                Object JavaDoc hint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
1212                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
1213                g.fillArc(x-diam, y-diam, 2*diam, 2*diam, angle, 180);
1214                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);
1215            }
1216        }
1217    }
1218    
1219    private int[] visualIntervalPosition(LayoutInterval interval, int dimension, int alignment) {
1220        int min = Short.MAX_VALUE;
1221        int max = Short.MIN_VALUE;
1222        if (interval.isParallel() && (interval.getGroupAlignment() != BASELINE)) {
1223            Iterator iter = interval.getSubIntervals();
1224            while (iter.hasNext()) {
1225                LayoutInterval subInterval = (LayoutInterval)iter.next();
1226                int imin, imax;
1227                int oppDim = (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL;
1228                if (LayoutInterval.isPlacedAtBorder(subInterval, oppDim, alignment)) {
1229                    if (subInterval.isParallel()) {
1230                        int[] ipos = visualIntervalPosition(subInterval, dimension, alignment);
1231                        imin = ipos[0]; imax = ipos[1];
1232                    } else if (!subInterval.isEmptySpace()) {
1233                        LayoutRegion region = subInterval.getCurrentSpace();
1234                        imin = region.positions[dimension][LEADING];
1235                        imax = region.positions[dimension][TRAILING];
1236                    } else {
1237                        imin = min; imax = max;
1238                    }
1239                } else {
1240                    imin = min; imax = max;
1241                }
1242                if (min > imin) min = imin;
1243                if (max < imax) max = imax;
1244            }
1245        }
1246        if (!interval.isParallel() || (min == Short.MAX_VALUE)) {
1247            LayoutRegion region = interval.getCurrentSpace();
1248            min = region.positions[dimension][LEADING];
1249            max = region.positions[dimension][TRAILING];
1250        }
1251        return new int[] {min, max};
1252    }
1253
1254    // -----
1255
// LayoutModel.Listener implementation & related
1256

1257    class Listener implements LayoutModel.Listener {
1258        private boolean active = false;
1259        
1260        public void layoutChanged(LayoutEvent ev) {
1261            if (!layoutModel.isUndoRedoInProgress()) {
1262                deactivate();
1263                LayoutDesigner.this.layoutChanged(ev);
1264                activate();
1265            }
1266        }
1267        void activate() {
1268            layoutModel.addListener(this);
1269            active = true;
1270        }
1271        void deactivate() {
1272            layoutModel.removeListener(this);
1273            active = false;
1274        }
1275        
1276        boolean isActive() {
1277            return active;
1278        }
1279    };
1280
1281    private void layoutChanged(LayoutEvent ev) {
1282        if (ev.getType() == LayoutEvent.INTERVAL_REMOVED) {
1283            // component interval was removed - need to clear neighbor gaps etc.
1284
LayoutInterval interval = ev.getInterval();
1285            LayoutComponent comp = interval.getComponent();
1286            if (comp != null) {
1287                int dim = -1;
1288                for (int i=0; i < DIM_COUNT; i++) {
1289                    if (comp.getLayoutInterval(i) == interval) {
1290                        dim = i;
1291                        break;
1292                    }
1293                }
1294                assert dim > -1;
1295                intervalRemoved(ev.getParentInterval(),
1296                                ev.getIndex(),
1297                                true,
1298                                LayoutInterval.wantResize(ev.getInterval()),
1299                                dim);
1300                if (comp.getParent() != null) {
1301                    updateDesignModifications(comp.getParent().getLayoutRoot(dim), dim);
1302                    // [maybe set imposeGaps or optimizeStructure to true if can't
1303
// ensure that removing handles gaps properly]
1304
visualStateUpToDate = false;
1305                }
1306            }
1307        }
1308    }
1309
1310    // -----
1311

1312    private boolean isComponentResizable(LayoutComponent comp, int dimension) {
1313        boolean[] res = comp.getResizability();
1314        if (res == null) {
1315            res = visualMapper.getComponentResizability(comp.getId(), new boolean[DIM_COUNT]);
1316            comp.setResizability(res);
1317        }
1318        return res[dimension];
1319    }
1320
1321    /**
1322     * Changes global alignment of the layout component.
1323     *
1324     * @param comp component whose alignment should be changed.
1325     * @param dimension dimension the alignment should be applied in.
1326     * @param alignment desired alignment.
1327     */

1328    public void adjustComponentAlignment(LayoutComponent comp, int dimension, int alignment) {
1329        if (logTestCode()) {
1330            testCode.add("// > ADJUST COMPONENT ALIGNMENT"); //NOI18N
1331
testCode.add("{"); //NOI18N
1332
testCode.add("LayoutComponent comp = model.getLayoutComponent(\"" + comp.getId() + "\");"); //NOI18N
1333
testCode.add("int dimension = " + dimension); //NOI18N
1334
testCode.add("int alignment = " + alignment); //NOI18N
1335
testCode.add("ld.adjustComponentAlignment(comp, dimension, alignment);"); //NOI18N
1336
testCode.add("}"); //NOI18N
1337
}
1338        modelListener.deactivate();
1339        LayoutInterval interval = comp.getLayoutInterval(dimension);
1340        
1341        // Skip non-resizable groups
1342
LayoutInterval parent = interval.getParent();
1343        while (parent != null) {
1344            if (!LayoutInterval.canResize(parent)) {
1345                interval = parent;
1346            }
1347            parent = parent.getParent();
1348        }
1349        assert !LayoutInterval.wantResize(interval);
1350        
1351        boolean changed = false;
1352        parent = interval.getParent();
1353        while (parent != null) {
1354            if (parent.isParallel()) {
1355                if (LayoutInterval.wantResize(parent) && !LayoutInterval.wantResize(interval)) {
1356                    int alg = interval.getAlignment();
1357                    if (alg != alignment) {
1358                        // Add fixed gap and change alignment
1359
int size = LayoutInterval.getIntervalCurrentSize(parent, dimension)
1360                            - LayoutInterval.getIntervalCurrentSize(interval, dimension);
1361                        if (size > 0) {
1362                            if (!interval.isSequential()) {
1363                                LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
1364                                layoutModel.setIntervalAlignment(interval, DEFAULT);
1365                                int i = layoutModel.removeInterval(interval);
1366                                layoutModel.addInterval(interval, seq, -1);
1367                                layoutModel.addInterval(seq, parent, i);
1368                                interval = seq;
1369                            }
1370                            int index = (alg == LEADING) ? -1 : 0;
1371                            LayoutInterval gap = new LayoutInterval(SINGLE);
1372                            gap.setSize(size);
1373                            layoutModel.addInterval(gap, interval, index);
1374                        }
1375                        layoutModel.setIntervalAlignment(interval, alignment);
1376                    }
1377                    changed = true;
1378                }
1379            } else {
1380                boolean before = true;
1381                boolean seqChanged = false;
1382                for (int i=0; i<parent.getSubIntervalCount(); i++) {
1383                    LayoutInterval li = parent.getSubInterval(i);
1384                    if (li == interval) {
1385                        before = false;
1386                    } else if (LayoutInterval.wantResize(li)) {
1387                        if ((before && (alignment == LEADING)) || (!before && (alignment == TRAILING))) {
1388                            assert li.isEmptySpace();
1389                            setIntervalResizing(li, false);
1390                            if (li.getPreferredSize() == 0) {
1391                                layoutModel.removeInterval(li);
1392                                i--;
1393                            }
1394                            seqChanged = true;
1395                        }
1396                    }
1397                }
1398                if (!changed && seqChanged) {
1399                    boolean insertGap = false;
1400                    int index = parent.indexOf(interval);
1401                    if (alignment == LEADING) {
1402                        if (parent.getSubIntervalCount() <= index+1) {
1403                            insertGap = true;
1404                            index = -1;
1405                        } else {
1406                            index++;
1407                            LayoutInterval candidate = parent.getSubInterval(index);
1408                            if (candidate.isEmptySpace()) {
1409                                setIntervalResizing(candidate, true);
1410                            } else {
1411                                insertGap = true;
1412                            }
1413                        }
1414                    } else {
1415                        assert (alignment == TRAILING);
1416                        if (index == 0) {
1417                            insertGap = true;
1418                        } else {
1419                            LayoutInterval candidate = parent.getSubInterval(index-1);
1420                            if (candidate.isEmptySpace()) {
1421                                setIntervalResizing(candidate, true);
1422                            } else {
1423                                insertGap = true;
1424                            }
1425                        }
1426                    }
1427                    if (insertGap) {
1428                        LayoutInterval gap = new LayoutInterval(SINGLE);
1429                        setIntervalResizing(gap, true);
1430                        layoutModel.setIntervalSize(gap, 0, 0, gap.getMaximumSize());
1431                        layoutModel.addInterval(gap, parent, index);
1432                    }
1433                    changed = true;
1434                }
1435            }
1436            interval = parent;
1437            parent = parent.getParent();
1438        }
1439        updateDesignModifications(interval, dimension);
1440        modelListener.activate();
1441        visualStateUpToDate = false;
1442        if (logTestCode()) {
1443            testCode.add("// < ADJUST COMPONENT ALIGNMENT"); //NOI18N
1444
}
1445    }
1446
1447    /**
1448     * Returns alignment of the component as the first item of the array.
1449     * The second item of the array indicates whether the alignment can
1450     * be changed to leading or trailing (e.g. if the current value is not
1451     * enforced by other resizable components). The returned alignment is
1452     * global e.g. it shows which edge of the container the component will track.
1453     *
1454     * @param comp component whose alignment should be determined.
1455     * @param dimension dimension in which the alignment should be determined.
1456     * @return alignment (or -1 if the component doesn't have a global alignment)
1457     * as the first item of the array and (canBeChangedToLeading ? 1 : 0) +
1458     * (canBeChangedToTrailing ? 2 : 0) as the second item.
1459     */

1460    public int[] getAdjustableComponentAlignment(LayoutComponent comp, int dimension) {
1461        LayoutInterval interval = comp.getLayoutInterval(dimension);
1462        boolean leadingFixed = true;
1463        boolean trailingFixed = true;
1464        boolean leadingAdjustable = true;
1465        boolean trailingAdjustable = true;
1466        
1467        if (LayoutInterval.wantResize(interval)) {
1468            leadingFixed = trailingFixed = leadingAdjustable = trailingAdjustable = false;
1469        }
1470        LayoutInterval parent = interval.getParent();
1471        while (parent != null) {
1472            if (!LayoutInterval.canResize(parent)) {
1473                leadingFixed = trailingFixed = leadingAdjustable = trailingAdjustable = true;
1474            } else if (parent.isParallel()) {
1475                if (LayoutInterval.wantResize(parent) && !LayoutInterval.wantResize(interval)) {
1476                    int alignment = interval.getAlignment();
1477                    if (alignment == LEADING) {
1478                        trailingFixed = false;
1479                    } else if (alignment == TRAILING) {
1480                        leadingFixed = false;
1481                    }
1482                }
1483            } else {
1484                boolean before = true;
1485                Iterator iter = parent.getSubIntervals();
1486                while (iter.hasNext()) {
1487                    LayoutInterval li = (LayoutInterval)iter.next();
1488                    if (li == interval) {
1489                        before = false;
1490                    } else if (LayoutInterval.wantResize(li)) {
1491                        boolean space = li.isEmptySpace();
1492                        if (before) {
1493                            leadingFixed = false;
1494                            if (!space) {
1495                                leadingAdjustable = false;
1496                            }
1497                        } else {
1498                            trailingFixed = false;
1499                            if (!space) {
1500                                trailingAdjustable = false;
1501                            }
1502                        }
1503                    }
1504                }
1505            }
1506            interval = parent;
1507            parent = parent.getParent();
1508        }
1509        int adjustable = (leadingAdjustable ? 1 << LEADING : 0) + (trailingAdjustable ? 1 << TRAILING : 0);
1510        if (leadingFixed && trailingFixed) {
1511            // As if top level group wantResize()
1512
if (LEADING == interval.getGroupAlignment()) {
1513                trailingFixed = false;
1514            } else {
1515                leadingFixed = false;
1516            }
1517        }
1518        int alignment;
1519        if (leadingFixed) {
1520            // !trailingFixed
1521
alignment = LEADING;
1522        } else {
1523            if (trailingFixed) {
1524                alignment = TRAILING;
1525            } else {
1526                alignment = -1;
1527            }
1528        }
1529        return new int[] {alignment, adjustable};
1530    }
1531
1532    /**
1533     * Determines whether the component is resizing in the given direction.
1534     *
1535     * @param comp component whose resizability should be determined.
1536     * @param dimension dimension in which the resizability should be determined.
1537     * @return <code>true</code> if the component is resizing, returns
1538     * <code>false</code> otherwise.
1539     */

1540    public boolean isComponentResizing(LayoutComponent comp, int dimension) {
1541        LayoutInterval interval = comp.getLayoutInterval(dimension);
1542        boolean fill = interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL);
1543        return fill ? false : LayoutInterval.wantResizeInLayout(interval);
1544    }
1545
1546    /**
1547     * Returns preferred size of the given interval (in pixels).
1548     *
1549     * @param interval interval whose preferred size should be determined.
1550     * @return preferred size of the given interval.
1551     */

1552    private int prefSizeOfInterval(LayoutInterval interval) {
1553        int dimension = -1;
1554        if (interval.isComponent()) {
1555            LayoutComponent comp = interval.getComponent();
1556            dimension = (interval == comp.getLayoutInterval(HORIZONTAL)) ? HORIZONTAL : VERTICAL;
1557            if (comp.isLinkSized(dimension)) {
1558                Collection linked = (Collection)layoutModel.getLinkSizeGroups(dimension).get(new Integer JavaDoc(comp.getLinkSizeId(dimension)));
1559                Iterator iter = linked.iterator();
1560                int prefSize = 0;
1561                while (iter.hasNext()) {
1562                    String JavaDoc compId = (String JavaDoc)iter.next();
1563                    LayoutComponent component = layoutModel.getLayoutComponent(compId);
1564                    LayoutInterval intr = component.getLayoutInterval(dimension);
1565                    int pref = intr.getPreferredSize();
1566                    if (pref == NOT_EXPLICITLY_DEFINED) {
1567                        Dimension JavaDoc prefDim = visualMapper.getComponentPreferredSize(compId);
1568                        pref = (dimension == HORIZONTAL) ? prefDim.width : prefDim.height;
1569                    }
1570                    prefSize = Math.max(pref, prefSize);
1571                }
1572                return prefSize;
1573            }
1574        }
1575        int prefSize = interval.getPreferredSize();
1576        if (prefSize == NOT_EXPLICITLY_DEFINED) {
1577            if (interval.isComponent()) {
1578                LayoutComponent comp = interval.getComponent();
1579                Dimension JavaDoc pref = visualMapper.getComponentPreferredSize(comp.getId());
1580                return (dimension == HORIZONTAL) ? pref.width : pref.height;
1581            } else if (interval.isEmptySpace()) {
1582                return sizeOfEmptySpace(interval);
1583            } else {
1584                assert interval.isGroup();
1585                prefSize = 0;
1586                Iterator iter = interval.getSubIntervals();
1587                if (interval.isSequential()) {
1588                    while (iter.hasNext()) {
1589                        LayoutInterval subInterval = (LayoutInterval)iter.next();
1590                        prefSize += prefSizeOfInterval(subInterval);
1591                    }
1592                } else {
1593                    while (iter.hasNext()) {
1594                        LayoutInterval subInterval = (LayoutInterval)iter.next();
1595                        prefSize = Math.max(prefSize, prefSizeOfInterval(subInterval));
1596                    }
1597                }
1598            }
1599        }
1600        return prefSize;
1601    }
1602
1603    /**
1604     * Returns size of the empty space represented by the given layout interval.
1605     *
1606     * @param interval layout interval that represents padding.
1607     * @return size of the padding.
1608     */

1609    private int sizeOfEmptySpace(LayoutInterval interval) {
1610        return LayoutUtils.getSizeOfDefaultGap(interval, visualMapper);
1611    }
1612
1613    /**
1614     * Sets component resizability. Makes the component resizing or fixed.
1615     *
1616     * @param comp component whose resizability should be set.
1617     * @param dimension dimension in which the resizability should be changed.
1618     * @param resizable determines whether the component should be made
1619     * resizable in the given dimension.
1620     */

1621    public void setComponentResizing(LayoutComponent comp, int dimension, boolean resizing) {
1622        if (logTestCode()) {
1623            testCode.add("// > SET COMPONENT RESIZING"); //NOI18N
1624
testCode.add("{"); //NOI18N
1625
testCode.add("LayoutComponent comp = lm.getLayoutComponent(\"" + comp.getId() + "\");"); //NOI18N
1626
testCode.add("int dimension = " + dimension + ";"); //NOI18N
1627
testCode.add("boolean resizing = " + resizing + ";"); //NOI18N
1628
testCode.add("ld.setComponentResizing(comp, dimension, resizing);"); //NOI18N
1629
testCode.add("}"); //NOI18N
1630
}
1631        modelListener.deactivate();
1632        LayoutInterval interval = comp.getLayoutInterval(dimension);
1633        
1634        // Unset the same-size if we are making the component resizable
1635
if (resizing && comp.isLinkSized(dimension)) {
1636            Collection linked = (Collection)layoutModel.getLinkSizeGroups(dimension).get(new Integer JavaDoc(comp.getLinkSizeId(dimension)));
1637            Collection toChange;
1638            if (linked.size() == 2) { // The second component will be unlinked, too.
1639
toChange = linked;
1640            } else {
1641                toChange = Collections.singletonList(comp.getId());
1642            }
1643            Iterator iter = toChange.iterator();
1644            while (iter.hasNext()) {
1645                String JavaDoc compId = (String JavaDoc)iter.next();
1646                LayoutComponent component = layoutModel.getLayoutComponent(compId);
1647                LayoutInterval intr = component.getLayoutInterval(dimension);
1648                Dimension JavaDoc prefDim = visualMapper.getComponentPreferredSize(compId);
1649                int prefSize = (dimension == HORIZONTAL) ? prefDim.width : prefDim.height;
1650                int currSize = intr.getCurrentSpace().size(dimension);
1651                if (currSize == prefSize) {
1652                    currSize = NOT_EXPLICITLY_DEFINED;
1653                }
1654                layoutModel.setIntervalSize(intr, intr.getMinimumSize(), currSize, intr.getMaximumSize());
1655            }
1656        }
1657        
1658        LayoutInterval parent = interval.getParent();
1659        boolean fill = interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL);
1660        boolean formerFill = interval.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL);
1661        if (fill || formerFill) {
1662            switchFillAttribute(interval, resizing);
1663        } else {
1664            setIntervalResizing(interval, resizing);
1665        }
1666        int delta = 0;
1667        if (!resizing) {
1668            int currSize = LayoutInterval.getIntervalCurrentSize(interval, dimension);
1669            int prefSize = prefSizeOfInterval(interval);
1670            delta = currSize - prefSize;
1671            if (delta != 0) {
1672                layoutModel.setIntervalSize(interval, interval.getMinimumSize(), currSize, interval.getMaximumSize());
1673            }
1674        }
1675        LayoutInterval intr = interval;
1676        LayoutInterval par = parent;
1677        while (par != null) {
1678            if (par.isParallel() && resizing) {
1679                int groupCurrSize = LayoutInterval.getIntervalCurrentSize(par, dimension);
1680                int currSize = LayoutInterval.getIntervalCurrentSize(intr, dimension);
1681                // PENDING currSize could change if groupPrefSize != groupCurrSize
1682
if (groupCurrSize != currSize) {
1683                    LayoutInterval seqGroup = intr;
1684                    LayoutInterval space = new LayoutInterval(SINGLE);
1685                    space.setSize(groupCurrSize - currSize);
1686                    int alignment = intr.getAlignment();
1687                    int index = (alignment == LEADING) ? -1 : 0;
1688                    if (intr.isSequential()) {
1689                        int spaceIndex = (alignment == LEADING) ? intr.getSubIntervalCount()-1 : 0;
1690                        LayoutInterval adjacentSpace = intr.getSubInterval(spaceIndex);
1691                        if (adjacentSpace.isEmptySpace()) {
1692                            int spaceSize = LayoutInterval.getIntervalCurrentSize(adjacentSpace, dimension);
1693                            layoutModel.removeInterval(adjacentSpace);
1694                            space.setSize(groupCurrSize - currSize + spaceSize);
1695                        }
1696                    } else {
1697                        seqGroup = new LayoutInterval(SEQUENTIAL);
1698                        layoutModel.setIntervalAlignment(intr, DEFAULT);
1699                        seqGroup.setAlignment(alignment);
1700                        int i = layoutModel.removeInterval(intr);
1701                        layoutModel.addInterval(intr, seqGroup, -1);
1702                        layoutModel.addInterval(seqGroup, par, i);
1703                    }
1704                    layoutModel.addInterval(space, seqGroup, index);
1705                    seqGroup.getCurrentSpace().set(dimension, par.getCurrentSpace());
1706                }
1707            } else if (par.isSequential()) {
1708                // Change resizability of gaps
1709
boolean parentSeq = (parent == par);
1710                List resizableList = new LinkedList();
1711                int alignment = parentSeq ? LayoutInterval.getEffectiveAlignment(interval) : 0;
1712                LayoutInterval leadingGap = null;
1713                LayoutInterval trailingGap = null;
1714                boolean afterDefining = false;
1715                Iterator iter = par.getSubIntervals();
1716                while (iter.hasNext()) {
1717                    LayoutInterval candidate = (LayoutInterval)iter.next();
1718                    if (candidate == interval) {
1719                        afterDefining = true;
1720                    }
1721                    if (candidate.isEmptySpace()) {
1722                        if (resizing) {
1723                            setIntervalResizing(candidate, false);
1724                            int currSize = LayoutInterval.getIntervalCurrentSize(candidate, dimension);
1725                            int prefSize = prefSizeOfInterval(candidate);
1726                            if (currSize != prefSize) {
1727                                layoutModel.setIntervalSize(candidate, candidate.getMinimumSize(),
1728                                    currSize, candidate.getMaximumSize());
1729                                delta += currSize - prefSize;
1730                            }
1731                        } else if (parentSeq) {
1732                            boolean wasFill = candidate.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL);
1733                            boolean glue = (candidate.getPreferredSize() != NOT_EXPLICITLY_DEFINED);
1734                            if (wasFill) {
1735                                trailingGap = candidate;
1736                            } else if ((trailingGap == null) || (!trailingGap.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL))) {
1737                                if (glue) {
1738                                    trailingGap = candidate;
1739                                } else {
1740                                    if (afterDefining && ((trailingGap == null) || (trailingGap.getPreferredSize() == NOT_EXPLICITLY_DEFINED))) {
1741                                        trailingGap = candidate;
1742                                    }
1743                                }
1744                            }
1745                            if ((leadingGap == null) && !afterDefining) {
1746                                leadingGap = candidate;
1747                            } else {
1748                                if ((wasFill && ((leadingGap == null) || (!leadingGap.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL))))
1749                                    || glue && ((leadingGap == null) || (!leadingGap.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL)
1750                                    && (leadingGap.getPreferredSize() == NOT_EXPLICITLY_DEFINED)))) {
1751                                    leadingGap = candidate;
1752                                }
1753                            }
1754                        }
1755                    } else {
1756                        if (candidate.getMaximumSize() == Short.MAX_VALUE) {
1757                            resizableList.add(candidate);
1758                        }
1759                    }
1760                }
1761                if (resizableList.size() > 0) {
1762                    iter = resizableList.iterator();
1763                    delta = (LayoutInterval.getIntervalCurrentSize(par, dimension) - prefSizeOfInterval(par) + delta)/resizableList.size();
1764                    while (iter.hasNext()) {
1765                        LayoutInterval candidate = (LayoutInterval)iter.next();
1766                        if (candidate.isGroup()) {
1767                            // PENDING currSize could change - we can't modify prefSize of group directly
1768
} else {
1769                            if (candidate == interval) {
1770                                if (delta != 0) {
1771                                    int prefSize = prefSizeOfInterval(candidate);
1772                                    layoutModel.setIntervalSize(candidate, candidate.getMinimumSize(),
1773                                        Math.max(0, prefSize - delta), candidate.getMaximumSize());
1774                                }
1775                            } else {
1776                                int currSize = LayoutInterval.getIntervalCurrentSize(candidate, dimension);
1777                                layoutModel.setIntervalSize(candidate, candidate.getMinimumSize(),
1778                                    Math.max(0, currSize - delta), candidate.getMaximumSize());
1779                            }
1780                        }
1781                    }
1782                }
1783                if (parentSeq) {
1784                    if (!LayoutInterval.wantResize(par)) {
1785                        LayoutInterval gap = null;
1786                        if ((alignment == TRAILING) && (leadingGap != null)) {
1787                            gap = leadingGap;
1788                            setIntervalResizing(leadingGap, !resizing);
1789                            layoutModel.changeIntervalAttribute(leadingGap, LayoutInterval.ATTRIBUTE_FILL, true);
1790                        }
1791                        if ((alignment == LEADING) && (trailingGap != null)) {
1792                            gap = trailingGap;
1793                            setIntervalResizing(trailingGap, !resizing);
1794                            layoutModel.changeIntervalAttribute(trailingGap, LayoutInterval.ATTRIBUTE_FILL, true);
1795                        }
1796                        if ((gap != null) && (delta != 0) && (gap.getPreferredSize() != NOT_EXPLICITLY_DEFINED)) {
1797                            layoutModel.setIntervalSize(gap, gap.getMinimumSize(),
1798                                Math.max(0, gap.getPreferredSize() - delta), gap.getMaximumSize());
1799                        }
1800                    }
1801                    parent = par.getParent(); // use parallel parent for group resizing check
1802
}
1803            }
1804            intr = par;
1805            par = par.getParent();
1806        }
1807        
1808        // Unset the same size once all changes in gap sizes are done
1809
if (resizing) {
1810            layoutModel.unsetSameSize(Collections.singletonList(comp.getId()), dimension);
1811        }
1812        modelListener.activate();
1813
1814        if (resizing) {
1815            // cancel possible suppressed resizing
1816
while (parent != null) {
1817                if (!LayoutInterval.canResize(parent)) {
1818                    operations.enableGroupResizing(parent);
1819                }
1820                parent = parent.getParent();
1821            }
1822        } else {
1823            // check if we should suppress resizing
1824
while (parent != null) {
1825                if (fillResizable(parent)) {
1826                    operations.suppressGroupResizing(parent);
1827                    parent = parent.getParent();
1828                } else {
1829                    break;
1830                }
1831            }
1832        }
1833
1834        updateDesignModifications(comp.getParent());
1835        visualStateUpToDate = false;
1836        if (logTestCode()) {
1837        testCode.add("// < SET COMPONENT RESIZING"); //NOI18N
1838
}
1839    }
1840    
1841    private boolean fillResizable(LayoutInterval interval) {
1842        if (!LayoutInterval.canResize(interval)) {
1843            return false;
1844        }
1845        if (interval.isGroup()) {
1846            boolean subres = true;
1847            Iterator it = interval.getSubIntervals();
1848            while (it.hasNext()) {
1849                LayoutInterval li = (LayoutInterval)it.next();
1850                if (LayoutInterval.wantResize(li) && !fillResizable(li)) {
1851                    subres = false;
1852                    break;
1853                }
1854            }
1855            return subres;
1856        } else {
1857            return interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL);
1858        }
1859    }
1860
1861    /**
1862     * Aligns given components in the specified direction.
1863     *
1864     * @param componentIds IDs of components that should be aligned.
1865     * @param closed determines if closed group should be created.
1866     * @param dimension dimension to align in.
1867     * @param alignment requested alignment.
1868     */

1869    public void align(Collection componentIds, boolean closed, int dimension, int alignment) {
1870        if (logTestCode()) {
1871            testCode.add("// > ALIGN"); //NOI18N
1872
testCode.add("{"); //NOI18N
1873
LayoutTestUtils.writeCollection(testCode, "componentIds", componentIds); //NOI18N
1874
testCode.add("boolean closed = " + closed + ";"); //NOI18N
1875
testCode.add("int dimension = " + dimension + ";"); //NOI18N
1876
testCode.add("int alignment = " + alignment + ";"); //NOI18N
1877
testCode.add("ld.align(componentIds, closed, dimension, alignment);"); //NOI18N
1878
testCode.add("}"); //NOI18N
1879
}
1880        LayoutInterval[] intervals = new LayoutInterval[componentIds.size()];
1881        int counter = 0;
1882        Iterator iter = componentIds.iterator();
1883        while (iter.hasNext()) {
1884            String JavaDoc id = (String JavaDoc)iter.next();
1885            LayoutComponent component = layoutModel.getLayoutComponent(id);
1886            intervals[counter++] = component.getLayoutInterval(dimension);
1887        }
1888        modelListener.deactivate();
1889        try {
1890            new LayoutAligner(this, layoutModel, operations).alignIntervals(intervals, closed, dimension, alignment);
1891        } finally {
1892            modelListener.activate();
1893        }
1894        requireStructureOptimization();
1895        if (logTestCode()) {
1896        testCode.add("// < ALIGN"); //NOI18N
1897
}
1898    }
1899
1900    private void destroyRedundantGroups(Set updatedContainers) {
1901        Iterator it = layoutModel.getAllComponents();
1902        while (it.hasNext()) {
1903            LayoutComponent comp = (LayoutComponent) it.next();
1904            if (!comp.isLayoutContainer())
1905                continue;
1906            
1907            boolean updated = false;
1908            for (int dim=0; dim<DIM_COUNT; dim++) {
1909                LayoutInterval interval = comp.getLayoutRoot(dim);
1910                updated = updated || destroyRedundantGroups(interval);
1911            }
1912            if (updated) {
1913                updatedContainers.add(comp);
1914            }
1915        }
1916    }
1917    
1918    private boolean destroyRedundantGroups(LayoutInterval interval) {
1919        boolean updated = false;
1920        for (int i=interval.getSubIntervalCount()-1; i>=0; i--) {
1921            if (i >= interval.getSubIntervalCount()) continue;
1922            LayoutInterval subInterval = interval.getSubInterval(i);
1923            if (subInterval.isGroup()) {
1924                destroyRedundantGroups(subInterval);
1925                destroyGroupIfRedundant(subInterval, interval);
1926                updated |= (subInterval.getParent() == null);
1927            }
1928        }
1929        return updated;
1930    }
1931
1932    /**
1933     * Destroys the given group if it is redundant in the layout model.
1934     *
1935     * @param group group whose necessity should be checked.
1936     * @param boundary parent of the group that limits the changes that
1937     * should be made e.g. no changes outside of this group even if it were
1938     * itself redundant. Can be <code>null</code> if there's no boundary.
1939     */

1940    void destroyGroupIfRedundant(LayoutInterval group, LayoutInterval boundary) {
1941        if ((group == null) || (!group.isGroup()) || (group == boundary)) return;
1942        LayoutInterval parent = group.getParent();
1943        // Don't destroy root intervals
1944
if (parent == null) return;
1945
1946        // Remove empty groups
1947
if (group.getSubIntervalCount() == 0) {
1948            takeOutInterval(group, boundary);
1949            return;
1950        }
1951
1952        if (operations.dissolveRedundantGroup(group)) {
1953            destroyGroupIfRedundant(parent, boundary);
1954        }
1955    }
1956
1957    /**
1958     * Removes the given interval from the layout model. Consolidates
1959     * parent groups if necessary.
1960     *
1961     * @param interval interval that should be removed.
1962     * @param boundary parent of the group that limits the changes that
1963     * should be made e.g. no changes outside of this group even if it were
1964     * itself redundant. Can be <code>null</code> if there's no boundary.
1965     */

1966    void takeOutInterval(LayoutInterval interval, LayoutInterval boundary) {
1967        LayoutInterval parent = interval.getParent();
1968        int index = parent.indexOf(interval);
1969        List toRemove = new LinkedList();
1970        toRemove.add(interval);
1971        if (parent.isSequential()) {
1972            // Remove leading gap
1973
if (index > 0) {
1974                LayoutInterval li = parent.getSubInterval(index-1);
1975                if (li.isEmptySpace()) {
1976                    toRemove.add(li);
1977                }
1978            }
1979            // Remove trailing gap
1980
if (index+1 < parent.getSubIntervalCount()) {
1981                LayoutInterval li = parent.getSubInterval(index+1);
1982                if (li.isEmptySpace()) {
1983                    toRemove.add(li);
1984                }
1985            }
1986            // Add dummy gap if necessary
1987
if ((toRemove.size() == 3) && (parent.getSubIntervalCount() > 3)) {
1988                LayoutInterval gap = new LayoutInterval(SINGLE);
1989                if (interval.isComponent() && (interval.getComponent().getLayoutInterval(VERTICAL) == interval)) {
1990                    int alignment = LayoutInterval.getEffectiveAlignment(interval);
1991                    int size = 0;
1992                    for (int i=0; i<3; i++) {
1993                        size += LayoutInterval.getIntervalCurrentSize((LayoutInterval)toRemove.get(i), VERTICAL);
1994                    }
1995                    gap.setSizes(NOT_EXPLICITLY_DEFINED, size, (alignment == TRAILING) ? Short.MAX_VALUE : USE_PREFERRED_SIZE);
1996                }
1997                layoutModel.addInterval(gap, parent, index);
1998            }
1999        }
2000        Iterator iter = toRemove.iterator();
2001        while (iter.hasNext()) {
2002            LayoutInterval remove = (LayoutInterval)iter.next();
2003            layoutModel.removeInterval(remove);
2004        }
2005        // Consolidate parent
2006
destroyGroupIfRedundant(parent, boundary);
2007    }
2008
2009    // -----
2010

2011    // [should change to operations.addGroupContent]
2012
/**
2013     * Creates a remainder parallel group (remainder to a main group of
2014     * aligned intervals).
2015     * @param list the content of the group, output from 'extract' method
2016     * @param seq a sequential group where to add to
2017     * @param index the index of the main group in the sequence
2018     * @param position the position of the remainder group relative to the main
2019     * group (LEADING or TRAILING)
2020     * @param mainAlignment effective alignment of the main group (LEADING or
2021     * TRAILING or something else meaning not aligned)
2022     * @param dimension dimension the remainder group is created in.
2023     */

2024    void createRemainderGroup(List list, LayoutInterval seq,
2025                                      int index, int position, int mainAlignment, int dimension)
2026    {
2027        assert seq.isSequential() && (position == LEADING || position == TRAILING);
2028        if (position == TRAILING) {
2029            index++;
2030        }
2031        // [revisit the way how spaces are handled - in accordance to optimizeGaps]
2032

2033        LayoutInterval gap = null;
2034        LayoutInterval leadingGap = null;
2035        LayoutInterval trailingGap = null;
2036        boolean onlyGaps = true;
2037        boolean gapLeads = true;
2038        boolean gapTrails = true;
2039
2040        // Remove sequences just with one gap
2041
for (int i = list.size()-1; i>=0; i--) {
2042            List subList = (List)list.get(i);
2043            if (subList.size() == 2) { // there is just one interval
2044
int alignment = ((Integer JavaDoc)subList.get(0)).intValue();
2045                LayoutInterval li = (LayoutInterval) subList.get(1);
2046                if (li.isEmptySpace()) {
2047                    if (gap == null || li.getMaximumSize() > gap.getMaximumSize()) {
2048                        gap = li;
2049                    }
2050                    if (isFixedPadding(li)) {
2051                        if (alignment == LEADING) {
2052                            leadingGap = li;
2053                            gapTrails = false;
2054                        }
2055                        else if (alignment == TRAILING) {
2056                            trailingGap = li;
2057                            gapLeads = false;
2058                        }
2059                    }
2060                    else {
2061                        gapLeads = false;
2062                        gapTrails = false;
2063                    }
2064                    list.remove(i);
2065                }
2066                else {
2067                    onlyGaps = false;
2068                }
2069            }
2070        }
2071
2072        if (list.size() == 1) { // just one sequence, need not a group
2073
List subList = (List) list.get(0);
2074            Iterator itr = subList.iterator();
2075            itr.next(); // skip alignment
2076
do {
2077                LayoutInterval li = (LayoutInterval) itr.next();
2078                layoutModel.addInterval(li, seq, index++);
2079            }
2080            while (itr.hasNext());
2081            return;
2082        }
2083
2084        // find common ending gaps, possibility to eliminate some...
2085
for (Iterator it=list.iterator(); it.hasNext(); ) {
2086            List subList = (List) it.next();
2087            if (subList.size() != 2) { // there are more intervals (will form a sequential group)
2088
onlyGaps = false;
2089
2090                boolean first = true;
2091                Iterator itr = subList.iterator();
2092                itr.next(); // skip seq. alignment
2093
do {
2094                    LayoutInterval li = (LayoutInterval) itr.next();
2095                    if (first) {
2096                        first = false;
2097                        if (isFixedPadding(li))
2098                            leadingGap = li;
2099                        else
2100                            gapLeads = false;
2101                    }
2102                    else if (!itr.hasNext()) {
2103                        if (isFixedPadding(li))
2104                            trailingGap = li;
2105                        else
2106                            gapTrails = false;
2107                    }
2108                }
2109                while (itr.hasNext());
2110            }
2111        }
2112
2113        if (onlyGaps) {
2114            operations.insertGapIntoSequence(gap, seq, index, dimension);
2115            return;
2116        }
2117
2118        // create group
2119
LayoutInterval group = new LayoutInterval(PARALLEL);
2120        if (position == mainAlignment) {
2121            // [but this should eliminate resizability only for gaps...]
2122
group.setMinimumSize(USE_PREFERRED_SIZE);
2123            group.setMaximumSize(USE_PREFERRED_SIZE);
2124        }
2125// group.setGroupAlignment(alignment);
2126

2127        // fill the group
2128
for (Iterator it=list.iterator(); it.hasNext(); ) {
2129            List subList = (List) it.next();
2130
2131            if (gapLeads) {
2132                subList.remove(1);
2133            }
2134            if (gapTrails) {
2135                subList.remove(subList.size()-1);
2136            }
2137
2138            LayoutInterval interval;
2139            if (subList.size() == 2) { // there is just one interval - use it directly
2140
int alignment = ((Integer JavaDoc)subList.get(0)).intValue();
2141                interval = (LayoutInterval) subList.get(1);
2142                if (alignment == LEADING || alignment == TRAILING) {
2143                    layoutModel.setIntervalAlignment(interval, alignment);
2144                }
2145            }
2146            else { // there are more intervals - group them in a sequence
2147
interval = new LayoutInterval(SEQUENTIAL);
2148                Iterator itr = subList.iterator();
2149                int alignment = ((Integer JavaDoc)itr.next()).intValue();
2150                if (alignment == LEADING || alignment == TRAILING) {
2151                    interval.setAlignment(alignment);
2152                }
2153                do {
2154                    LayoutInterval li = (LayoutInterval) itr.next();
2155                    layoutModel.addInterval(li, interval, -1);
2156                }
2157                while (itr.hasNext());
2158            }
2159            layoutModel.addInterval(interval, group, -1);
2160        }
2161
2162        // add the group to the sequence
2163
if (gapLeads) {
2164            layoutModel.addInterval(leadingGap, seq, index++);
2165        }
2166        layoutModel.addInterval(group, seq, index++);
2167        if (gapTrails) {
2168            layoutModel.addInterval(trailingGap, seq, index);
2169        }
2170    }
2171
2172    static boolean isFixedPadding(LayoutInterval interval) {
2173        return interval.isEmptySpace()
2174               && (interval.getMinimumSize() == NOT_EXPLICITLY_DEFINED || interval.getMinimumSize() == USE_PREFERRED_SIZE)
2175               && interval.getPreferredSize() == NOT_EXPLICITLY_DEFINED
2176               && (interval.getMaximumSize() == NOT_EXPLICITLY_DEFINED || interval.getMaximumSize() == USE_PREFERRED_SIZE);
2177    }
2178
2179    // -----
2180

2181    // requires the layout image up-to-date (all positions known)
2182
// requires the group contains some component (at least indirectly)
2183
private int optimizeGaps(LayoutInterval group, int dimension, boolean recursive) {
2184        assert group.isParallel();
2185
2186        // sub-groups first (not using iterator, intervals may change)
2187
if (recursive) {
2188            for (int i=0; i < group.getSubIntervalCount(); i++) {
2189                LayoutInterval li = group.getSubInterval(i);
2190                if (li.isParallel()) {
2191                    optimizeGaps(li, dimension, recursive);
2192                }
2193                else if (li.isSequential()) {
2194                    for (int ii=0; ii < li.getSubIntervalCount(); ii++) {
2195                        LayoutInterval llii = li.getSubInterval(ii);
2196                        if (llii.isParallel()) {
2197                            int idx = optimizeGaps(llii, dimension, recursive);
2198                            if (idx >= 0) // position in sequence changed (a gap inserted)
2199
ii = idx;
2200                        }
2201                    }
2202                }
2203            }
2204        }
2205
2206        if (group.getGroupAlignment() == CENTER || group.getGroupAlignment() == BASELINE) {
2207            return -1;
2208        }
2209        int nonEmptyCount = LayoutInterval.getCount(group, LayoutRegion.ALL_POINTS, true);
2210        if (nonEmptyCount <= 1) {
2211            if (group.getParent() == null) {
2212                if (group.getSubIntervalCount() > 1) {
2213                    // [removing container supporting gap]
2214
for (int i=group.getSubIntervalCount()-1; i >= 0; i--) {
2215                        if (group.getSubInterval(i).isEmptySpace()) {
2216                            layoutModel.removeInterval(group, i);
2217                            break;
2218                        }
2219                    }
2220                }
2221                else if (group.getSubIntervalCount() == 0) {
2222                    // [sort of hack - would be nice the filling gap is ensured somewhere else]
2223
propEmptyContainer(group, dimension);
2224                }
2225            } else { // [dissolving one-member group should not be here]
2226
assert (nonEmptyCount == 1);
2227                assert (group.getSubIntervalCount() == 1);
2228                LayoutInterval interval = group.getSubInterval(0);
2229                //int alignment = interval.getAlignment();
2230
layoutModel.removeInterval(interval);
2231                layoutModel.setIntervalAlignment(interval, group.getAlignment());
2232                LayoutInterval parent = group.getParent();
2233                int index = layoutModel.removeInterval(group);
2234                if (parent.isSequential() && interval.isSequential()) {
2235                    // dissolve the sequential group in its parent
2236
for (int i=interval.getSubIntervalCount()-1; i>=0; i--) {
2237                        LayoutInterval subInterval = interval.getSubInterval(i);
2238                        layoutModel.removeInterval(subInterval);
2239                        layoutModel.addInterval(subInterval, parent, index);
2240                    }
2241                    eliminateConsecutiveGaps(parent, 0, dimension);
2242                } else {
2243                    layoutModel.addInterval(interval, parent, index);
2244                }
2245/* if (offsetGap != null) {
2246                    // Reinsert/simulate behaviour of the removed offset gap.
2247                    int size;
2248                    if (parent.isSequential()) {
2249                        size = LayoutInterval.getIntervalCurrentSize(group, dimension)
2250                            - LayoutInterval.getIntervalCurrentSize(interval, dimension);
2251                        if (alignment == LEADING) index++;
2252                    } else {
2253                        size = LayoutInterval.getIntervalCurrentSize(parent, dimension);
2254                        index = -1;
2255                    }
2256                    layoutModel.setIntervalSize(offsetGap, offsetGap.getMinimumSize(), size, offsetGap.getMaximumSize());
2257                    layoutModel.addInterval(offsetGap, parent, index);
2258                    eliminateConsecutiveGaps(parent, 0, dimension);
2259                } */

2260            }
2261            return -1;
2262        }
2263
2264        return operations.optimizeGaps(group, dimension);
2265    }
2266
2267    // -----
2268

2269    /**
2270     * Sets interval resizability. Makes it resizing or fixed.
2271     *
2272     * @param interval layout interval whose resizability should be set.
2273     * @param resizable determines whether the passed interval should be made resizable.
2274     */

2275    void setIntervalResizing(LayoutInterval interval, boolean resizable) {
2276        switchFillAttribute(interval, resizable);
2277        layoutModel.setIntervalSize(interval,
2278            resizable ? NOT_EXPLICITLY_DEFINED : USE_PREFERRED_SIZE,
2279            interval.getPreferredSize(),
2280            resizable ? Short.MAX_VALUE : USE_PREFERRED_SIZE);
2281    }
2282
2283    // Changes fill attributes when interval becomes (non-)resizable.
2284
private void switchFillAttribute(LayoutInterval interval, boolean resizable) {
2285        if (resizable) {
2286            if (interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL)) {
2287                layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FORMER_FILL, true);
2288                layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FILL, false);
2289            }
2290        } else {
2291            if (interval.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL)) {
2292                layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FORMER_FILL, false);
2293                layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FILL, true);
2294            }
2295        }
2296    }
2297
2298    // -----
2299

2300    public void setDefaultSize(String JavaDoc compId) {
2301        if (logTestCode()) {
2302            testCode.add("// > SET DEFAULT SIZE"); //NOI18N
2303
testCode.add("{"); //NOI18N
2304
testCode.add("String compId = \"${" + compId + "}\";"); //NOI18N
2305
testCode.add("ld.setDefaultSize(compId);"); //NOI18N
2306
testCode.add("}"); //NOI18N
2307
}
2308        LayoutComponent component = layoutModel.getLayoutComponent(compId);
2309        if (component != null)
2310            setDefaultSize(component);
2311        if (logTestCode()) {
2312            testCode.add("// < SET DEFAULT SIZE"); //NOI18N
2313
}
2314    }
2315
2316    private void setDefaultSize(LayoutComponent component) {
2317        imposeSize = true;
2318        if (component.isLayoutContainer()) {
2319            for (Iterator it=component.getSubcomponents(); it.hasNext(); ) {
2320                LayoutComponent comp = (LayoutComponent) it.next();
2321                if (comp.isLayoutContainer())
2322                    setDefaultSize(comp);
2323            }
2324            setDefaultSizeInContainer(component.getLayoutRoot(HORIZONTAL));
2325            setDefaultSizeInContainer(component.getLayoutRoot(VERTICAL));
2326            updateDesignModifications(component);
2327        }
2328        else {
2329            operations.resizeInterval(component.getLayoutInterval(HORIZONTAL), NOT_EXPLICITLY_DEFINED);
2330            operations.resizeInterval(component.getLayoutInterval(VERTICAL), NOT_EXPLICITLY_DEFINED);
2331        }
2332    }
2333
2334    private void setDefaultSizeInContainer(LayoutInterval interval) {
2335        if (!interval.isGroup()) {
2336            if (LayoutInterval.canResize(interval))
2337                operations.resizeInterval(interval,
2338                        interval.getMinimumSize() != USE_PREFERRED_SIZE ? interval.getMinimumSize() : NOT_EXPLICITLY_DEFINED);
2339        }
2340        else {
2341            for (Iterator it=interval.getSubIntervals(); it.hasNext(); ) {
2342                setDefaultSizeInContainer((LayoutInterval)it.next());
2343            }
2344        }
2345    }
2346
2347    private void updateDesignModifications(LayoutComponent container) {
2348        if (imposeSize || optimizeStructure) {
2349            // additional update (after layout) is going to happen - so now just
2350
// clean the design attrs, container resizing gap will be found later
2351
cleanDesignAttrs(container.getLayoutRoot(HORIZONTAL));
2352            cleanDesignAttrs(container.getLayoutRoot(VERTICAL));
2353        }
2354        else {
2355            updateDesignModifications(container.getLayoutRoot(HORIZONTAL), HORIZONTAL);
2356            updateDesignModifications(container.getLayoutRoot(VERTICAL), VERTICAL);
2357        }
2358    }
2359
2360    private void updateDesignModifications(LayoutInterval root, int dimension) {
2361        cleanDesignAttrs(root);
2362        findContainerResizingGap(root, dimension);
2363    }
2364
2365    private static void cleanDesignAttrs(LayoutInterval group) {
2366        group.unsetAttribute(LayoutInterval.DESIGN_ATTRS);
2367        for (int i=0,n=group.getSubIntervalCount(); i < n; i++) {
2368            LayoutInterval li = group.getSubInterval(i);
2369            if (li.isGroup())
2370                cleanDesignAttrs(li);
2371            else
2372                li.unsetAttribute(LayoutInterval.DESIGN_ATTRS);
2373        }
2374    }
2375
2376    private void findContainerResizingGap(LayoutInterval rootInterval, int dimension) {
2377        if (!LayoutInterval.wantResize(rootInterval) && // See issue 66849
2378
(LayoutInterval.getIntervalCurrentSize(rootInterval, dimension) != prefSizeOfInterval(rootInterval))) {
2379            // Resizing gap would change the layout
2380
return;
2381        }
2382        // find gap for container resizing
2383
int gapPosition = TRAILING;
2384        LayoutInterval resGap = findContainerResizingGap(rootInterval, dimension, gapPosition);
2385        if (resGap == null) {
2386            gapPosition = LEADING;
2387            resGap = findContainerResizingGap(rootInterval, dimension, gapPosition);
2388            if (resGap == null) {
2389                gapPosition = -1;
2390                resGap = findContainerResizingGap(rootInterval, dimension, gapPosition);
2391                if (resGap == null) {
2392                    return;
2393                }
2394            }
2395        }
2396        else if (!LayoutInterval.canResize(resGap)) { // we prefer resizing gaps
2397
LayoutInterval gap = findContainerResizingGap(rootInterval, dimension, LEADING);
2398            if (gap != null && LayoutInterval.canResize(gap)) {
2399                resGap = gap;
2400                gapPosition = LEADING;
2401            }
2402            else {
2403                gap = findContainerResizingGap(rootInterval, dimension, -1);
2404                if (gap != null && LayoutInterval.canResize(gap)) {
2405                    resGap = gap;
2406                    gapPosition = -1;
2407                }
2408            }
2409        }
2410
2411        // mark the gap and all surrounding intervals
2412
resGap.setAttribute(LayoutInterval.ATTR_DESIGN_CONTAINER_GAP
2413                            | LayoutInterval.ATTR_DESIGN_RESIZING);
2414
2415        LayoutInterval sub = resGap;
2416        LayoutInterval parent = resGap.getParent();
2417        do {
2418            if (parent.isSequential()) {
2419                for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) {
2420                    LayoutInterval li = (LayoutInterval) it.next();
2421                    if (li != sub) {
2422                        li.setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING);
2423                    }
2424                }
2425            }
2426            else { // parallel parent
2427
for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) {
2428                    LayoutInterval interval = (LayoutInterval) it.next();
2429                    if (interval != sub) {
2430                        assert interval.isSequential();
2431                        if (interval.isSequential()) {
2432                            for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) {
2433                                LayoutInterval li = interval.getSubInterval(i);
2434                                if (((i == 0 && gapPosition == LEADING)
2435                                     || (i+1 == n && gapPosition == TRAILING))
2436                                    && canBeContainerResizingGap(li))
2437                                { // parallel resizing gap
2438
li.setAttribute(LayoutInterval.ATTR_DESIGN_RESIZING);
2439                                }
2440                                else li.setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING);
2441                            }
2442                        }
2443                        else {
2444                            interval.setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING);
2445                        }
2446                    }
2447                }
2448            }
2449            sub = parent;
2450            parent = sub.getParent();
2451        }
2452        while (parent != null);
2453    }
2454
2455    private static LayoutInterval findContainerResizingGap(LayoutInterval group, int dimension, int alignment) {
2456        assert group.isParallel();
2457
2458        LayoutInterval theGap = null;
2459        int gapSize = Integer.MAX_VALUE;
2460
2461        for (Iterator it=group.getSubIntervals(); it.hasNext(); ) {
2462            LayoutInterval seq = (LayoutInterval) it.next();
2463            if (!seq.isSequential()) {
2464                return null;
2465            }
2466
2467            int n = seq.getSubIntervalCount();
2468            if (alignment == LEADING || alignment == TRAILING) {
2469                // [check for the same resizability of the ending gaps]
2470
LayoutInterval li = seq.getSubInterval(alignment == LEADING ? 0 : n-1);
2471                LayoutInterval gap;
2472                if (canBeContainerResizingGap(li)
2473                    && (LayoutInterval.wantResize(seq)
2474                        || LayoutInterval.getEffectiveAlignment(li) == (alignment^1)))
2475                { // making this gap resizing won't change the visual appearance of the sequence
2476
gap = li;
2477                }
2478                else if (li.isParallel()) {
2479                    gap = findContainerResizingGap(li, dimension, alignment);
2480                }
2481                else gap = null; // not an ending gap
2482

2483                if (gap == null) {
2484                    return null;
2485                }
2486
2487                LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(gap, alignment^1, false);
2488                int p1 = neighbor.getCurrentSpace().positions[dimension][alignment];
2489                int p2 = group.getCurrentSpace().positions[dimension][alignment];
2490                int size = Math.abs(p2-p1);
2491
2492                if (theGap == null || size < gapSize) {
2493                    theGap = gap;
2494                    gapSize = size;
2495                }
2496// if (LayoutInterval.canResize(gap) != LayoutInterval.canResize(theGap)) {
2497
// return null;
2498
// }
2499
}
2500            else { // somewhere in the middle - can be just one
2501
for (int i=n-2; i > 0; i--) {
2502                    LayoutInterval li = seq.getSubInterval(i);
2503                    if (canBeContainerResizingGap(li) && LayoutInterval.canResize(li)) {
2504                        return group.getSubIntervalCount() == 1 ? li : null;
2505                    }
2506                }
2507                return null;
2508            }
2509        }
2510
2511        return theGap;
2512    }
2513
2514    private static boolean canBeContainerResizingGap(LayoutInterval li) {
2515        return li.isEmptySpace()
2516               && (li.getPreferredSize() != NOT_EXPLICITLY_DEFINED || li.getMaximumSize() >= Short.MAX_VALUE);
2517    }
2518
2519    /**
2520     * @param resizingDef if provided the size change is caused "internally"
2521     * (mouse operation driven by LayoutDragger)
2522     */

2523    private boolean imposeCurrentContainerSize(LayoutComponent component, LayoutDragger.SizeDef[] resizingDef, boolean recursive) {
2524        assert component.isLayoutContainer();
2525
2526        Rectangle JavaDoc interior = visualMapper.getContainerInterior(component.getId());
2527        if (interior == null)
2528            return false; // this container is not built
2529
component.setCurrentInterior(interior);
2530
2531        if (component.getParent() != null) {
2532            Rectangle JavaDoc bounds = visualMapper.getComponentBounds(component.getId());
2533            component.setCurrentBounds(bounds,
2534                visualMapper.getBaselinePosition(component.getId(), bounds.width, bounds.height));
2535        }
2536        for (Iterator it=component.getSubcomponents(); it.hasNext(); ) {
2537            LayoutComponent subComp = (LayoutComponent) it.next();
2538            Rectangle JavaDoc bounds = visualMapper.getComponentBounds(subComp.getId());
2539            subComp.setCurrentBounds(bounds,
2540                                     visualMapper.getBaselinePosition(subComp.getId(), bounds.width, bounds.height));
2541            if (subComp.isLayoutContainer()) {
2542                if (recursive)
2543                    imposeCurrentContainerSize(subComp, null, true);
2544            }
2545            else imposeCurrentComponentSize(subComp);
2546        }
2547        Dimension JavaDoc minimum = null;
2548        Dimension JavaDoc preferred = null;
2549        for (int i=0; i < DIM_COUNT; i++) {
2550            LayoutInterval outer = component.getLayoutInterval(i);
2551            int currentSize = outer.getCurrentSpace().size(i);
2552            LayoutInterval root = component.getLayoutRoot(i);
2553            if (root.getSubIntervalCount() == 0) { // empty root group - add a filling gap
2554
propEmptyContainer(root, i);
2555            }
2556            else { // not empty, there can be a resizing gap inside the container
2557
if (resizingDef != null && resizingDef[i] != null) {
2558                    LayoutInterval resGap = resizingDef[i].getResizingGap();
2559                    if (resGap != null) { // this is it (special gap for design time resizing)
2560
int size = resizingDef[i].getResizingGapSize(currentSize);
2561                        if (size == 0) { // remove the gap
2562
LayoutInterval gapParent = resGap.getParent();
2563                            assert gapParent.isSequential();
2564                            int index = layoutModel.removeInterval(resGap);
2565                            assert index == 0 || index == gapParent.getSubIntervalCount();
2566                            if (gapParent.getSubIntervalCount() == 1) {
2567                                LayoutInterval last = layoutModel.removeInterval(gapParent, 0);
2568                                operations.addContent(last, gapParent.getParent(), layoutModel.removeInterval(gapParent));
2569                            }
2570                            else if (LayoutInterval.canResize(resGap) && !LayoutInterval.wantResize(root)) {
2571                                // don't lose resizability of the layout
2572
index = index == 0 ? gapParent.getSubIntervalCount()-1 : 0;
2573                                LayoutInterval otherGap = gapParent.getSubInterval(index);
2574                                if (otherGap.isEmptySpace()) { // the gap should be resizing
2575
layoutModel.setIntervalSize(otherGap,
2576                                        NOT_EXPLICITLY_DEFINED, otherGap.getPreferredSize(), Short.MAX_VALUE);
2577                                }
2578                            }
2579                        }
2580                        else { // set gap size
2581
operations.resizeInterval(resGap, size);
2582                            if (size == NOT_EXPLICITLY_DEFINED && LayoutInterval.canResize(resGap)) {
2583                                // hack: eliminate unnecessary resizing of default padding gap
2584
resGap.setMaximumSize(USE_PREFERRED_SIZE);
2585                                boolean layoutResizing = LayoutInterval.wantResize(root);
2586                                resGap.setMaximumSize(Short.MAX_VALUE);
2587                                if (layoutResizing) { // the gap should be fixed
2588
layoutModel.setIntervalSize(resGap,
2589                                        NOT_EXPLICITLY_DEFINED, NOT_EXPLICITLY_DEFINED, USE_PREFERRED_SIZE);
2590                                }
2591                            }
2592                        }
2593                    }
2594                    else if (!LayoutInterval.wantResize(root)) {
2595                        // no resizing gap in fixed layout of resizing container
2596
int minLayoutSize = computeMinimumDesignSize(root);
2597                        int growth = root.getCurrentSpace().size(i) - minLayoutSize;
2598                        if (growth > 0) { // add new resizing gap at the end to hold the new extra space
2599
LayoutInterval endGap = new LayoutInterval(SINGLE);
2600                            endGap.setSizes(NOT_EXPLICITLY_DEFINED, growth, Short.MAX_VALUE);
2601                            operations.insertGap(endGap, root, minLayoutSize, i, TRAILING);
2602                        }
2603                    }
2604                }
2605                updateLayoutStructure(root, i, true);
2606            }
2607
2608            if (component.getParent() != null) {
2609                if (minimum == null) {
2610                    minimum = visualMapper.getComponentMinimumSize(component.getId());
2611                    preferred = visualMapper.getComponentPreferredSize(component.getId());
2612                }
2613                int min = i == HORIZONTAL ? minimum.width : minimum.height;
2614                boolean externalSize =
2615                    (visualMapper.hasExplicitPreferredSize(component.getId())
2616                     && currentSize != (i==HORIZONTAL ? preferred.width:preferred.height))
2617                    || currentSize < min
2618                    || (currentSize > min && !LayoutInterval.wantResize(root));
2619                operations.resizeInterval(outer, externalSize ? currentSize : NOT_EXPLICITLY_DEFINED);
2620            }
2621        }
2622
2623        return true;
2624    }
2625
2626    private void imposeCurrentComponentSize(LayoutComponent component) {
2627        Dimension JavaDoc preferred = visualMapper.getComponentPreferredSize(component.getId());
2628        for (int i=0; i < DIM_COUNT; i++) {
2629            LayoutInterval li = component.getLayoutInterval(i);
2630            int defPref = li.getPreferredSize();
2631            if (LayoutInterval.wantResizeInLayout(li) && defPref != 0) { // == 0 subordinate component (filling)
2632
// resizing component with size-defining role in parent
2633
int current = li.getCurrentSpace().size(i);
2634                int pref = i == HORIZONTAL ? preferred.width : preferred.height;
2635                if (defPref == NOT_EXPLICITLY_DEFINED)
2636                    defPref = pref;
2637                if (defPref != current)
2638                    operations.resizeInterval(li, current != pref ? current : NOT_EXPLICITLY_DEFINED);
2639            }
2640        }
2641    }
2642
2643    private void imposeCurrentGapSize(LayoutInterval gap, int currentSize, int dimension) {
2644        int pad = -1;
2645        int min = gap.getMinimumSize();
2646        int pref = gap.getPreferredSize();
2647        if (pref == NOT_EXPLICITLY_DEFINED) {
2648            if (!LayoutInterval.wantResizeInLayout(gap))
2649                return; // don't change default gap if not resizing
2650
pad = LayoutUtils.getSizeOfDefaultGap(gap, visualMapper);
2651            pref = pad;
2652        }
2653        if (currentSize != pref) { // [check for canResize?]
2654
if (min == NOT_EXPLICITLY_DEFINED) {
2655                if (pad < 0) {
2656                    pad = LayoutUtils.getSizeOfDefaultGap(gap, visualMapper);
2657                }
2658                min = pad;
2659            }
2660            else if (min == USE_PREFERRED_SIZE) {
2661                min = pref;
2662            }
2663            if (currentSize < min) {
2664                currentSize = min;
2665            }
2666            operations.resizeInterval(gap, currentSize == pad ? NOT_EXPLICITLY_DEFINED : currentSize);
2667        }
2668    }
2669
2670    private void propEmptyContainer(LayoutInterval root, int dimension) {
2671        assert root.getParent() == null && root.getSubIntervalCount() == 0;
2672        LayoutInterval gap = new LayoutInterval(SINGLE);
2673        gap.setSizes(0, root.getCurrentSpace().size(dimension), Short.MAX_VALUE);
2674        layoutModel.addInterval(gap, root, 0);
2675    }
2676
2677    private int computeMinimumDesignSize(LayoutInterval interval) {
2678        int size = 0;
2679        if (interval.isSingle()) {
2680            int min = interval.getMinimumSize(true);
2681            size = min == USE_PREFERRED_SIZE ? interval.getPreferredSize(true) : min;
2682            if (size == NOT_EXPLICITLY_DEFINED) {
2683                if (interval.isComponent()) {
2684                    LayoutComponent comp = interval.getComponent();
2685                    Dimension JavaDoc dim = min == USE_PREFERRED_SIZE ?
2686                                    visualMapper.getComponentPreferredSize(comp.getId()) :
2687                                    visualMapper.getComponentMinimumSize(comp.getId());
2688                    size = interval==comp.getLayoutInterval(HORIZONTAL) ? dim.width : dim.height;
2689                }
2690                else { // gap
2691
size = LayoutUtils.getSizeOfDefaultGap(interval, visualMapper);
2692                }
2693            }
2694        }
2695        else if (interval.isSequential()) {
2696            for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) {
2697                size += computeMinimumDesignSize(interval.getSubInterval(i));
2698            }
2699        }
2700        else { // parallel group
2701
for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) {
2702                size = Math.max(size, computeMinimumDesignSize(interval.getSubInterval(i)));
2703            }
2704        }
2705        return size;
2706    }
2707
2708    // -----
2709

2710    // recursive
2711
private void intervalRemoved(LayoutInterval parent, int index, boolean primary, boolean wasResizing, int dimension) {
2712        if (parent.isSequential()) {
2713            LayoutInterval leadingGap;
2714            LayoutInterval leadingNeighbor;
2715            if (index > 0) {
2716                LayoutInterval li = parent.getSubInterval(index-1);
2717                if (li.isEmptySpace()) {
2718                    leadingGap = li;
2719                    layoutModel.removeInterval(li);
2720                    index--;
2721                    leadingNeighbor = index > 0 ? parent.getSubInterval(index-1) : null;
2722                }
2723                else {
2724                    leadingGap = null;
2725                    leadingNeighbor = li;
2726                }
2727            }
2728            else {
2729                leadingGap = null;
2730                leadingNeighbor = null;
2731            }
2732
2733            LayoutInterval trailingGap;
2734            LayoutInterval trailingNeighbor;
2735            if (index < parent.getSubIntervalCount()) {
2736                LayoutInterval li = parent.getSubInterval(index);
2737                if (li.isEmptySpace()) {
2738                    trailingGap = li;
2739                    layoutModel.removeInterval(li);
2740                    trailingNeighbor = index < parent.getSubIntervalCount() ?
2741                                       parent.getSubInterval(index) : null;
2742                }
2743                else {
2744                    trailingGap = null;
2745                    trailingNeighbor = li;
2746                }
2747            }
2748            else {
2749                trailingGap = null;
2750                trailingNeighbor = null;
2751            }
2752
2753            if (!wasResizing
2754                && ((leadingGap != null && LayoutInterval.canResize(leadingGap))
2755                    || (trailingGap != null && LayoutInterval.canResize(trailingGap))))
2756                wasResizing = true;
2757
2758            LayoutInterval superParent = parent.getParent();
2759
2760            // [check for last interval (count==1), if parallel superParent try to re-add the interval]
2761
if (parent.getSubIntervalCount() == 0) { // nothing remained
2762
int idx = layoutModel.removeInterval(parent);
2763                if (superParent.getParent() != null) {
2764                    intervalRemoved(superParent, idx, false, wasResizing, dimension);
2765                }
2766                else if (superParent.getSubIntervalCount() == 0) { // empty root group - add a filling gap
2767
propEmptyContainer(superParent, dimension);
2768                }
2769            }
2770            else { // the sequence remains
2771
boolean restResizing = LayoutInterval.contentWantResize(parent);
2772                if (wasResizing && !restResizing) {
2773                    if (leadingNeighbor == null && parent.getAlignment() == LEADING) {
2774                        layoutModel.setIntervalAlignment(parent, TRAILING);
2775                    }
2776                    if (trailingNeighbor == null && parent.getAlignment() == TRAILING) {
2777                        layoutModel.setIntervalAlignment(parent, LEADING);
2778                    }
2779                }
2780
2781                int cutSize = LayoutRegion.distance(
2782                        (leadingNeighbor != null ? leadingNeighbor : parent).getCurrentSpace(),
2783                        (trailingNeighbor != null ? trailingNeighbor : parent).getCurrentSpace(),
2784                        dimension,
2785                        leadingNeighbor != null ? TRAILING : LEADING,
2786                        trailingNeighbor != null ? LEADING : TRAILING);
2787
2788                if ((leadingNeighbor != null && trailingNeighbor != null) // inside a sequence
2789
|| superParent.getParent() == null // in root parallel group
2790
|| (leadingNeighbor != null && LayoutInterval.getEffectiveAlignment(leadingNeighbor, TRAILING) == TRAILING)
2791                    || (trailingNeighbor != null && LayoutInterval.getEffectiveAlignment(trailingNeighbor, LEADING) == LEADING))
2792                { // create a placeholder gap
2793
int min, max;
2794                    if (wasResizing && !restResizing) { // the gap should be resizing
2795
min = NOT_EXPLICITLY_DEFINED;
2796                        max = Short.MAX_VALUE;
2797                    }
2798                    else {
2799                        min = max = USE_PREFERRED_SIZE;
2800                    }
2801                    LayoutInterval gap = new LayoutInterval(SINGLE);
2802                    gap.setSizes(min, cutSize, max);
2803                    layoutModel.addInterval(gap, parent, index);
2804                }
2805                else { // this is an "open" end - compensate the size in the parent
2806
if (parent.getSubIntervalCount() == 1) {
2807                        LayoutInterval last = layoutModel.removeInterval(parent, 0);
2808                        layoutModel.addInterval(last, superParent, layoutModel.removeInterval(parent));
2809                        layoutModel.setIntervalAlignment(last, parent.getRawAlignment());
2810                    }
2811                    else { // adjust current space of the parent sequence
2812
// (border interval at the open end removed)
2813
int l = (trailingNeighbor != null && leadingNeighbor == null ?
2814                            trailingNeighbor : parent).getCurrentSpace().positions[dimension][LEADING];
2815                        int t = (leadingNeighbor != null && trailingNeighbor == null ?
2816                            leadingNeighbor : parent).getCurrentSpace().positions[dimension][TRAILING];
2817                        parent.getCurrentSpace().set(dimension, l, t);
2818                    }
2819                    maintainSize(superParent, wasResizing || restResizing, dimension,
2820                                 parent, parent.getCurrentSpace().size(dimension) - cutSize);
2821                }
2822
2823                if (wasResizing && !restResizing) {
2824                    operations.enableGroupResizing(superParent); // in case it was disabled
2825
}
2826            }
2827        }
2828        else {
2829            if (parent.getParent() == null) return; // Component placed directly in the root interval
2830
assert parent.isParallel() && parent.getSubIntervalCount() > 0;
2831
2832            int groupAlign = parent.getGroupAlignment();
2833            if (primary && (groupAlign == LEADING || groupAlign == TRAILING)) {
2834                maintainSize(parent, wasResizing, dimension, null, 0);
2835            }
2836
2837            if (parent.getSubIntervalCount() == 1 && parent.getParent() != null) { // last interval in parallel group
2838
// cancel the group and move the interval up
2839
LayoutInterval remaining = parent.getSubInterval(0);
2840                layoutModel.removeInterval(remaining);
2841                layoutModel.setIntervalAlignment(remaining, parent.getAlignment());
2842                if (LayoutInterval.wantResize(remaining) && !LayoutInterval.canResize(parent)) {
2843                    // resizing interval in fixed group - make it fixed
2844
if (remaining.isGroup())
2845                        operations.suppressGroupResizing(remaining);
2846                    else
2847                        layoutModel.setIntervalSize(remaining,
2848                                USE_PREFERRED_SIZE, remaining.getPreferredSize(), USE_PREFERRED_SIZE);
2849                }
2850                LayoutInterval superParent = parent.getParent();
2851                int i = layoutModel.removeInterval(parent);
2852                operations.addContent(remaining, superParent, i);
2853                if (remaining.isSequential() && superParent.isSequential()) {
2854                    // eliminate possible directly consecutive gaps
2855
// [this could be done by the addContent method directly]
2856
eliminateConsecutiveGaps(superParent, i, dimension);
2857                }
2858                // [TODO if parallel superParent try to re-add the interval]
2859
}
2860            else if (wasResizing && !LayoutInterval.contentWantResize(parent)) {
2861                operations.enableGroupResizing(parent);
2862            }
2863        }
2864    }
2865    
2866    private void eliminateConsecutiveGaps(LayoutInterval group, int index, int dimension) {
2867        assert group.isSequential();
2868        if (index > 0)
2869            index--;
2870        while (index < group.getSubIntervalCount()-1) {
2871            LayoutInterval current = group.getSubInterval(index);
2872            LayoutInterval next = group.getSubInterval(index+1);
2873            if (current.isEmptySpace() && next.isEmptySpace()) {
2874                int la;
2875                LayoutRegion lr;
2876                if (index > 0) {
2877                    la = TRAILING;
2878                    lr = group.getSubInterval(index-1).getCurrentSpace();
2879                }
2880                else {
2881                    la = LEADING;
2882                    lr = group.getCurrentSpace();
2883                }
2884                int ta;
2885                LayoutRegion tr;
2886                if (index+2 < group.getSubIntervalCount()) {
2887                    ta = LEADING;
2888                    tr = group.getSubInterval(index+2).getCurrentSpace();
2889                }
2890                else {
2891                    ta = TRAILING;
2892                    tr = group.getCurrentSpace();
2893                }
2894                operations.eatGap(current, next, LayoutRegion.distance(lr, tr, dimension, la, ta));
2895            }
2896            else index++;
2897        }
2898    }
2899
2900
2901    private void maintainSize(LayoutInterval group, boolean wasResizing, int dimension,
2902                              LayoutInterval excluded, int excludedSize)
2903    {
2904        assert group.isParallel(); // [also not used for center or baseline groups]
2905

2906        int groupSize = group.getCurrentSpace().size(dimension);
2907        int[] groupPos = group.getCurrentSpace().positions[dimension];
2908
2909        boolean leadAlign = false;
2910        boolean trailAlign = false;
2911        int leadCompPos = Integer.MAX_VALUE;
2912        int trailCompPos = Integer.MIN_VALUE;
2913        int subSize = Integer.MIN_VALUE;
2914
2915        Iterator it = group.getSubIntervals();
2916        while (it.hasNext()) {
2917            LayoutInterval li = (LayoutInterval) it.next();
2918            int align = li.getAlignment();
2919            int l, t; // leading and trailing position of first and last component
2920
if (li != excluded) {
2921                int size = li.getCurrentSpace().size(dimension);
2922                if (size >= groupSize) {
2923                    return;
2924                }
2925                if (size > subSize) {
2926                    subSize = size;
2927                }
2928                l = LayoutUtils.getOutermostComponent(li, dimension, LEADING)
2929                        .getCurrentSpace().positions[dimension][LEADING];
2930                t = LayoutUtils.getOutermostComponent(li, dimension, TRAILING)
2931                        .getCurrentSpace().positions[dimension][TRAILING];
2932            }
2933            else {
2934                if (excludedSize > subSize) {
2935                    subSize = excludedSize;
2936                }
2937                if (align == LEADING) {
2938                    l = groupPos[LEADING];
2939                    t = groupPos[LEADING] + excludedSize;
2940                }
2941                else {// TRAILING
2942
l = groupPos[TRAILING] - excludedSize;
2943                    t = groupPos[TRAILING];
2944                }
2945            }
2946            if (l < leadCompPos)
2947                leadCompPos = l;
2948            if (t > trailCompPos)
2949                trailCompPos = t;
2950
2951            if (align == LEADING)
2952                leadAlign = true;
2953            else // TRAILING
2954
trailAlign = true;
2955        }
2956
2957        if (leadAlign && trailAlign) {
2958            optimizeGaps(group, dimension, false);
2959        }
2960        else { // one open edge to compensate
2961
if (!LayoutInterval.canResize(group)) { // resizing disabled on the group
2962
wasResizing = false;
2963            }
2964            boolean resizing = LayoutInterval.wantResize(group);
2965            LayoutInterval parent = group.getParent();
2966            if (parent != null && parent.isParallel()
2967                && group.getAlignment() == (leadAlign ? LEADING : TRAILING))
2968            { // the group can shrink and the parent compensate
2969
maintainSize(parent, wasResizing && !resizing, dimension, group, subSize);
2970                if (leadAlign) {
2971                    groupPos[TRAILING] = trailCompPos;
2972                }
2973                if (trailAlign) {
2974                    groupPos[LEADING] = leadCompPos;
2975                }
2976                groupPos[CENTER] = (groupPos[LEADING] + groupPos[LEADING]) / 2;
2977            }
2978            else {
2979                int increment = groupSize - subSize;
2980                assert increment > 0;
2981                LayoutInterval gap = new LayoutInterval(SINGLE);
2982                int min, max;
2983                if (!resizing && (wasResizing || parent == null)) {
2984                    min = NOT_EXPLICITLY_DEFINED;
2985                    max = Short.MAX_VALUE;
2986                }
2987                else {
2988                    min = max = USE_PREFERRED_SIZE;
2989                }
2990                gap.setSizes(min, increment, max);
2991
2992                operations.insertGap(gap, group, leadAlign ? trailCompPos : leadCompPos,
2993                                     dimension, leadAlign ? TRAILING : LEADING);
2994
2995                if (leadAlign) {
2996                    groupPos[TRAILING] = trailCompPos;
2997                }
2998                if (trailAlign) {
2999                    groupPos[LEADING] = leadCompPos;
3000                }
3001                groupPos[CENTER] = (groupPos[LEADING] + groupPos[LEADING]) / 2;
3002
3003                if (parent != null) {
3004                    if (parent.isSequential()) {
3005                        parent = parent.getParent();
3006                    }
3007                    optimizeGaps(parent, dimension, false);
3008                }
3009            }
3010        }
3011    }
3012
3013    public String JavaDoc[] positionCode() {
3014        return dragger.positionCode();
3015    }
3016
3017    // -----
3018
// auxiliary fields holding temporary objects used frequently
3019

3020    // converted cursor position used during moving/resizing
3021
private int[] cursorPos = { 0, 0 };
3022
3023    // resizability of a component in the designer
3024
private boolean[][] resizability = { { true, true }, { true, true } };
3025
3026    // -----
3027
// test generation support
3028

3029    static final String JavaDoc TEST_SWITCH = "netbeans.form.layout_test"; // NOI18N
3030

3031    /* stores test code lines */
3032    public List testCode = new ArrayList();
3033
3034    // 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)
3035
private List testCode0 = new ArrayList();
3036    private List beforeMove = new ArrayList();
3037    private List move1 = new ArrayList();
3038    private List move2 = new ArrayList();
3039    private boolean isMoving = false;
3040    
3041    private int modelCounter = -1;
3042    
3043    private Point JavaDoc lastMovePoint = new Point JavaDoc(0, 0);
3044
3045    public int getModelCounter() {
3046        return modelCounter;
3047    }
3048
3049    public void setModelCounter(int modelCounter) {
3050        this.modelCounter = modelCounter;
3051    }
3052
3053    public static boolean testMode() {
3054        return Boolean.getBoolean(TEST_SWITCH);
3055    }
3056
3057    public boolean logTestCode() {
3058        return modelCounter > -1 && Boolean.getBoolean(TEST_SWITCH);
3059    }
3060}
3061
Popular Tags