KickJava   Java API By Example, From Geeks To Geeks.

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


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 import java.util.*;
23
24 /**
25  * Aligning algorithms.
26  *
27  * @author Jan Stola
28  */

29 class LayoutAligner implements LayoutConstants {
30     /** Layout designer that invoked the layout aligner. */
31     private LayoutDesigner designer;
32     /** The corresponding layout model. */
33     private LayoutModel layoutModel;
34     /** The corresponding layout operations. */
35     private LayoutOperations operations;
36
37     /**
38      * Creates new <code>LayoutAligner</code>.
39      *
40      * @param designer layout designer to use.
41      * @param layoutModel layout model to use.
42      * @param operations layout operations to use.
43      */

44     LayoutAligner(LayoutDesigner designer, LayoutModel layoutModel, LayoutOperations operations) {
45         this.designer = designer;
46         this.layoutModel = layoutModel;
47         this.operations = operations;
48     }
49     
50     /**
51      * Aligns given components in the specified direction.
52      *
53      * @param componentIds IDs of components that should be aligned.
54      * @param closed determines if closed group should be created.
55      * @param dimension dimension to align in.
56      * @param alignment requested alignment.
57      */

58     void alignIntervals(LayoutInterval[] intervals, boolean closed, int dimension, int alignment) {
59         // Find nearest common (parallel) parent
60
LayoutInterval parParent = LayoutInterval.getCommonParent(intervals);
61         if (parParent.isSequential()) {
62             parParent = parParent.getParent();
63         }
64
65         // Divide layout intervals into pre/aligned/post parallel groups.
66
markByAlignAttributes(parParent, intervals);
67         List removedSeqs = new LinkedList();
68         parParent = splitByAlignAttrs(parParent, removedSeqs, alignment, closed, dimension, false);
69
70         // Transfer the intervals into common parallel parent
71
List gapsToResize = transferToParallelParent(intervals, parParent, alignment, closed);
72         LayoutInterval returnPar = LayoutInterval.getFirstParent(parParent, PARALLEL);
73         returnRemovedIntervals((returnPar == null) ? parParent : returnPar, removedSeqs, dimension);
74
75         if (alignment != CENTER) {
76             // Calculate leading and trailing intervals
77
Map leadingMap = new HashMap();
78             Map trailingMap = new HashMap();
79             for (int i=0; i<intervals.length; i++) {
80                 LayoutInterval interval = intervals[i];
81                 LayoutInterval parent = interval.getParent();
82                 // Special handling for standalone component intervals in parParent
83
if (parent == parParent) parent = interval;
84                 LayoutInterval leading = (LayoutInterval)leadingMap.get(parent);
85                 LayoutInterval trailing = (LayoutInterval)trailingMap.get(parent);
86                 if ((leading == null) || (parent.indexOf(leading) > parent.indexOf(interval))) {
87                     leadingMap.put(parent, interval);
88                 }
89                 if ((trailing == null) || (parent.indexOf(trailing) < parent.indexOf(interval))) {
90                     trailingMap.put(parent, interval);
91                 }
92             }
93             // Create arrays of leading/trailing intervals
94
LayoutInterval[] leadingIntervals = new LayoutInterval[leadingMap.size()];
95             LayoutInterval[] trailingIntervals = new LayoutInterval[trailingMap.size()];
96             Iterator iter = leadingMap.values().iterator();
97             int counter = 0;
98             while (iter.hasNext()) {
99                 LayoutInterval interval = (LayoutInterval)iter.next();
100                 leadingIntervals[counter++] = interval;
101             }
102             iter = trailingMap.values().iterator();
103             counter = 0;
104             while (iter.hasNext()) {
105                 LayoutInterval interval = (LayoutInterval)iter.next();
106                 trailingIntervals[counter++] = interval;
107             }
108
109             // Perform alignment of the intervals transfered into common parallel parent
110
if (closed) {
111                 align(leadingIntervals, trailingIntervals, true, dimension, alignment);
112             } else {
113                 LayoutInterval[] itervalsToAlign = (alignment == LEADING) ? leadingIntervals : trailingIntervals;
114                 align(itervalsToAlign, null, false, dimension, alignment);
115             }
116             
117             // Must be done after align() to keep original eff. alignment inside align() method
118
iter = gapsToResize.iterator();
119             while (iter.hasNext()) {
120                 LayoutInterval gap = (LayoutInterval)iter.next();
121                 designer.setIntervalResizing(gap, true);
122             }
123         }
124
125         // Do some clean up
126
designer.destroyGroupIfRedundant(parParent, null);
127     }
128
129     /**
130      * Marks layout intervals by <code>ATTR_ALIGN_PRE</code> and
131      * <code>ATTR_ALIGN_POST</code> attributes. The first one is assigned
132      * to the intervals that are in front of some from the passed
133      * <code>intervals</code>. The second one is assigned to the intervals
134      * that are behind some of the passed <code>intervals</code>.
135      * Note that some intervals may obtain both of them.
136      *
137      * @param parParent parallel parent of all passed intervals
138      * (the marking is restricted to this interval).
139      * @param intervals intervals according which the marking is done.
140      */

141     private void markByAlignAttributes(LayoutInterval parParent, LayoutInterval[] intervals) {
142         layoutModel.changeIntervalAttribute(parParent, LayoutInterval.ATTR_ALIGN_PRE, true);
143         layoutModel.changeIntervalAttribute(parParent, LayoutInterval.ATTR_ALIGN_POST, true);
144         for (int i=0; i<intervals.length; i++) {
145             LayoutInterval interval = intervals[i];
146             while (interval != parParent) {
147                 LayoutInterval parent = interval.getParent();
148                 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTR_ALIGN_PRE, true);
149                 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTR_ALIGN_POST, true);
150                 if (parent.isSequential()) {
151                     int index = parent.indexOf(interval);
152                     for (int j=0; j<parent.getSubIntervalCount(); j++) {
153                         if (j < index) {
154                             markByAlignAttribute(parent.getSubInterval(j), LayoutInterval.ATTR_ALIGN_PRE);
155                         } else if (j > index) {
156                             markByAlignAttribute(parent.getSubInterval(j), LayoutInterval.ATTR_ALIGN_POST);
157                         }
158                     }
159                 }
160                 interval = parent;
161             }
162         }
163     }
164
165     /**
166      * Marks the <code>interval</code> by the given attribute. If the interval
167      * is group, then all subintervals are marked as well.
168      *
169      * @param interval interval to mark.
170      * @param attr attr to be assigned.
171      */

172     private void markByAlignAttribute(LayoutInterval interval, int attr) {
173         layoutModel.changeIntervalAttribute(interval, attr, true);
174         if (interval.isGroup()) {
175             Iterator iter = interval.getSubIntervals();
176             while (iter.hasNext()) {
177                 markByAlignAttribute((LayoutInterval)iter.next(), attr);
178             }
179         }
180     }
181
182     private LayoutInterval splitByAlignAttrs(LayoutInterval interval, List removedSeqs, int alignment, boolean closed, int dimension, boolean optimize) {
183        if (interval.isGroup()) {
184             for (int i=interval.getSubIntervalCount()-1; i>=0; i--) {
185                 LayoutInterval subInterval = interval.getSubInterval(i);
186                 splitByAlignAttrs(subInterval, removedSeqs, alignment, closed, dimension, true);
187             }
188             if (interval.isParallel()) {
189                 if (interval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE)
190                     && interval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST)) {
191                     LayoutInterval prePar = new LayoutInterval(PARALLEL);
192                     layoutModel.changeIntervalAttribute(prePar, LayoutInterval.ATTR_ALIGN_PRE, true);
193                     LayoutInterval midPar = new LayoutInterval(PARALLEL);
194                     layoutModel.changeIntervalAttribute(midPar, LayoutInterval.ATTR_ALIGN_PRE, true);
195                     layoutModel.changeIntervalAttribute(midPar, LayoutInterval.ATTR_ALIGN_POST, true);
196                     LayoutInterval postPar = new LayoutInterval(PARALLEL);
197                     layoutModel.changeIntervalAttribute(postPar, LayoutInterval.ATTR_ALIGN_POST, true);
198
199                     // Calculate statistics for this parallel group
200
int minMid = Short.MAX_VALUE;
201                     int maxMid = Short.MIN_VALUE;
202                     int maxPre = Short.MIN_VALUE;
203                     int minPost = Short.MAX_VALUE;
204                     int maxMidWidth = 0;
205                     Iterator iter = interval.getSubIntervals();
206                     while (iter.hasNext()) {
207                         LayoutInterval subInterval = (LayoutInterval)iter.next();
208                         boolean preSub = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE);
209                         boolean postSub = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST);
210                         LayoutInterval trailingPre = null;
211                         LayoutInterval leadingMid = null;
212                         LayoutInterval trailingMid = null;
213                         LayoutInterval leadingPost = null;
214                         if (subInterval.isSequential() && preSub && postSub) {
215                             Iterator subIter = subInterval.getSubIntervals();
216                             while (subIter.hasNext()) {
217                                 LayoutInterval subSubInterval = (LayoutInterval)subIter.next();
218                                 boolean preSubSub = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE);
219                                 boolean postSubSub = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST);
220                                 if (preSubSub) {
221                                     if (postSubSub) {
222                                         if (leadingMid == null) {
223                                             leadingMid = subSubInterval;
224                                         }
225                                         trailingMid = subSubInterval;
226                                     } else {
227                                         if (!subSubInterval.isEmptySpace() || (subSubInterval.getPreferredSize() == NOT_EXPLICITLY_DEFINED)) {
228                                             trailingPre = subSubInterval;
229                                         }
230                                     }
231                                 } else if (postSubSub) {
232                                     if ((leadingPost == null) && (!subSubInterval.isEmptySpace() || (subSubInterval.getPreferredSize() == NOT_EXPLICITLY_DEFINED))) {
233                                         leadingPost = subSubInterval;
234                                     }
235                                 }
236                             }
237                         } else {
238                             if (preSub) {
239                                 if (postSub) {
240                                     leadingMid = subInterval;
241                                     trailingMid = subInterval;
242                                 } else {
243                                     trailingPre = subInterval;
244                                 }
245                             } else if (postSub) {
246                                 leadingPost = subInterval;
247                             }
248                         }
249                         
250                         if (trailingPre != null) {
251                             maxPre = Math.max(maxPre, getPosition(trailingPre, dimension, TRAILING));
252                         }
253                         if (leadingMid != null) { // implies trailingMid != null
254
int midLeading = getPosition(leadingMid, dimension, LEADING);
255                             int midTrailing = getPosition(trailingMid, dimension, TRAILING);
256                             int width = midTrailing - midLeading;
257                             minMid = Math.min(minMid, midLeading);
258                             maxMid = Math.max(maxMid, midTrailing);
259                             maxMidWidth = Math.max(maxMidWidth, width);
260                         }
261                         if (leadingPost != null) {
262                             minPost = Math.min(minPost, getPosition(leadingPost, dimension, LEADING));
263                         }
264                     }
265
266                     // Perform the split
267
for (int i=interval.getSubIntervalCount()-1; i>=0; i--) {
268                         LayoutInterval subInterval = interval.getSubInterval(i);
269                         int index = layoutModel.removeInterval(subInterval);
270                         if (subInterval.isSequential()) {
271                             if (!subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE)
272                                 && !subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST)) {
273                                 removedSeqs.add(subInterval);
274                                 continue;
275                             }
276                             LayoutInterval preSeq = new LayoutInterval(SEQUENTIAL);
277                             layoutModel.changeIntervalAttribute(preSeq, LayoutInterval.ATTR_ALIGN_PRE, true);
278                             LayoutInterval midSeq = new LayoutInterval(SEQUENTIAL);
279                             layoutModel.changeIntervalAttribute(midSeq, LayoutInterval.ATTR_ALIGN_PRE, true);
280                             layoutModel.changeIntervalAttribute(midSeq, LayoutInterval.ATTR_ALIGN_POST, true);
281                             LayoutInterval postSeq = new LayoutInterval(SEQUENTIAL);
282                             layoutModel.changeIntervalAttribute(postSeq, LayoutInterval.ATTR_ALIGN_POST, true);
283
284                             int[] leading = new int[subInterval.getSubIntervalCount()];
285                             int[] trailing = new int[leading.length];
286                             for (int j=0; j<subInterval.getSubIntervalCount(); j++) {
287                                 LayoutInterval subSubInterval = subInterval.getSubInterval(j);
288                                 leading[j] = getPosition(subSubInterval, dimension, LEADING);
289                                 trailing[j] = getPosition(subSubInterval, dimension, TRAILING);
290                             }
291
292                             // Update some gaps
293
LayoutInterval lastPre = null;
294                             LayoutInterval lastPreGap = null;
295                             LayoutInterval firstPostGap = null;
296                             LayoutInterval firstMid = null;
297                             LayoutInterval lastMid = null;
298                             LayoutInterval firstPost = null;
299                             for (int j=0; j<subInterval.getSubIntervalCount(); j++) {
300                                 LayoutInterval subSubInterval = subInterval.getSubInterval(j);
301                                 boolean pre = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE);
302                                 boolean post = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST);
303                                 if (pre && !post) {
304                                     lastPreGap = subSubInterval;
305                                 }
306                                 if (post && !pre) {
307                                     if (firstPostGap == null) {
308                                         firstPostGap = subSubInterval;
309                                     }
310                                 }
311                                 if (pre && post) {
312                                     if (firstMid == null) {
313                                         firstMid = subSubInterval;
314                                     }
315                                     lastMid = subSubInterval;
316                                 }
317                             }
318                             firstPost = firstPostGap;
319                             lastPre = lastPreGap;
320                             if ((firstPostGap != null) && !firstPostGap.isEmptySpace()) {
321                                 firstPostGap = null;
322                             }
323                             if ((lastPreGap != null) && !lastPreGap.isEmptySpace()) {
324                                 lastPreGap = null;
325                             }
326                             if (alignment == LEADING) {
327                                 int bias = Math.max(0, maxPre - minMid);
328                                 int shift = (LayoutInterval.getEffectiveAlignment(firstMid) == LEADING) ? bias : 0;
329                                 if (lastPreGap != null) {
330                                     int delta = shortenGap(lastPreGap, getPosition(firstMid, dimension, LEADING) - minMid - shift);
331                                     trailing[subInterval.indexOf(lastPreGap)] -= delta;
332                                 }
333                                 shift = (LayoutInterval.getEffectiveAlignment(lastMid) == LEADING) ? bias : 0;
334                                 if (firstPostGap != null) {
335                                     int delta = shortenGap(firstPostGap, maxMidWidth - (getPosition(lastMid, dimension, TRAILING) - minMid) + shift);
336                                     leading[subInterval.indexOf(firstPostGap)] += delta;
337                                 }
338                             }
339                             if (alignment == TRAILING) {
340                                 int bias = Math.max(0, maxMid - minPost);
341                                 int shift = (LayoutInterval.getEffectiveAlignment(lastMid) == TRAILING) ? bias : 0;
342                                 if (firstPostGap != null) {
343                                     int delta = shortenGap(firstPostGap, maxMid - getPosition(lastMid, dimension, TRAILING) - shift);
344                                     leading[subInterval.indexOf(firstPostGap)] += delta;
345                                 }
346                                 shift = (LayoutInterval.getEffectiveAlignment(firstMid) == TRAILING) ? bias : 0;
347                                 if (lastPreGap != null) {
348                                     int delta = shortenGap(lastPreGap, maxMidWidth - (maxMid - getPosition(firstMid, dimension, LEADING)) + shift);
349                                     trailing[subInterval.indexOf(lastPreGap)] -= delta;
350                                 }
351                             }
352                             
353                             // Set alignment of sequences
354
setAlignmentAccordingEffectiveAlignment(preSeq, lastPre);
355                             setAlignmentAccordingEffectiveAlignment(midSeq, firstMid);
356                             setAlignmentAccordingEffectiveAlignment(postSeq, firstPost);
357                             
358                             for (int j=subInterval.getSubIntervalCount()-1; j>=0; j--) {
359                                 LayoutInterval subSubInterval = subInterval.getSubInterval(j);
360                                 boolean pre = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE);
361                                 boolean post = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST);
362                                 assert pre || post;
363                                 LayoutInterval seqToInsertInto;
364                                 if (pre && !post && ((alignment == LEADING) || closed)) {
365                                     seqToInsertInto = preSeq;
366                                 } else if (post && !pre && ((alignment == TRAILING) || closed)) {
367                                     seqToInsertInto = postSeq;
368                                 } else {
369                                     seqToInsertInto = midSeq;
370                                 }
371                                 expandCurrentSpace(seqToInsertInto, dimension, leading[j], trailing[j]);
372                                 layoutModel.removeInterval(subSubInterval);
373                                 layoutModel.addInterval(subSubInterval, seqToInsertInto, 0);
374                             }
375                             putGroupToGroup(preSeq, prePar, 0);
376                             putGroupToGroup(midSeq, midPar, 0);
377                             putGroupToGroup(postSeq, postPar, 0);
378                         } else {
379                             boolean pre = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE);
380                             boolean post = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST);
381                             if (!pre && !post) {
382                                 removedSeqs.add(subInterval);
383                             } else if (pre && !post && ((alignment == LEADING) || closed)) {
384                                 layoutModel.addInterval(subInterval, prePar, 0);
385                                 prePar.getCurrentSpace().expand(subInterval.getCurrentSpace());
386                             } else if (post && !pre && ((alignment == TRAILING) || closed)) {
387                                 layoutModel.addInterval(subInterval, postPar, 0);
388                                 postPar.getCurrentSpace().expand(subInterval.getCurrentSpace());
389                             } else {
390                                 layoutModel.addInterval(subInterval, midPar, 0);
391                                 midPar.getCurrentSpace().expand(subInterval.getCurrentSpace());
392                             }
393                         }
394                     }
395                     LayoutInterval parent = interval.getParent();
396                     int index;
397                     if (parent != null) {
398                         index = layoutModel.removeInterval(interval);
399                     } else {
400                         parent = interval;
401                         index = 0;
402                     }
403                     if (!parent.isSequential()) {
404                         LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
405                         layoutModel.changeIntervalAttribute(seq, LayoutInterval.ATTR_ALIGN_PRE, true);
406                         layoutModel.changeIntervalAttribute(seq, LayoutInterval.ATTR_ALIGN_POST, true);
407                         layoutModel.addInterval(seq, parent, index);
408                         index = 0;
409                         parent = seq;
410                     }
411                     putGroupToGroup(postPar, parent, index);
412                     interval = putGroupToGroup(midPar, parent, index, optimize);
413                     putGroupToGroup(prePar, parent, index);
414                 }
415             }
416         }
417         return interval;
418     }
419
420     private LayoutInterval putGroupToGroup(LayoutInterval groupToInsert, LayoutInterval group, int index) {
421         return putGroupToGroup(groupToInsert, group, index, true);
422     }
423     
424     private LayoutInterval putGroupToGroup(LayoutInterval groupToInsert, LayoutInterval group, int index, boolean optimize) {
425         // Remove empty spaces from parallel group
426
if (groupToInsert.isParallel()) {
427             LayoutInterval emptySpace = null;
428             for (int i=groupToInsert.getSubIntervalCount()-1; i>=0; i--) {
429                 LayoutInterval interval = groupToInsert.getSubInterval(i);
430                 if (interval.isEmptySpace()) {
431                     emptySpace = interval;
432                     layoutModel.removeInterval(interval);
433                 }
434             }
435             if ((groupToInsert.getSubIntervalCount() == 0) && (emptySpace != null)) {
436                 // Use the last empty space as a replacement for the group - handled below
437
layoutModel.addInterval(emptySpace, groupToInsert, 0);
438             }
439         }
440         if (groupToInsert.getSubIntervalCount() > 0) {
441             LayoutRegion region = groupToInsert.getCurrentSpace();
442             while (optimize && (groupToInsert.getSubIntervalCount() == 1)) {
443                 LayoutInterval interval = groupToInsert.getSubInterval(0);
444                 layoutModel.removeInterval(interval);
445                 layoutModel.setIntervalAlignment(interval, groupToInsert.getAlignment());
446                 groupToInsert = interval;
447             }
448             if (optimize && groupToInsert.isSequential() && group.isSequential()) {
449                 for (int i=groupToInsert.getSubIntervalCount()-1; i>=0; i--) {
450                     LayoutInterval subInterval = groupToInsert.getSubInterval(i);
451                     layoutModel.removeInterval(subInterval);
452                     layoutModel.addInterval(subInterval, group, index);
453                 }
454                 groupToInsert = null;
455             } else {
456                 layoutModel.addInterval(groupToInsert, group, index);
457             }
458             group.getCurrentSpace().expand(region);
459         }
460         return groupToInsert;
461     }
462
463     /**
464      * Expands the current space of <code>interval</code>.
465      *
466      * @param interval interval whose current space should be expanded.
467      * @param dimension dimension in which the expansion occurs.
468      * @param leading lower bound of the expansion.
469      * @param trailing upper bound of the expansion.
470      */

471     private void expandCurrentSpace(LayoutInterval interval, int dimension, int leading, int trailing) {
472         LayoutRegion region = new LayoutRegion();
473         region.positions[dimension][LEADING] = leading;
474         region.positions[dimension][TRAILING] = trailing;
475         interval.getCurrentSpace().expand(region, dimension);
476     }
477     
478     private int getPosition(LayoutInterval interval, int dimension, int alignment) {
479         if (interval.isEmptySpace()) {
480             LayoutInterval parent = interval.getParent();
481             assert parent.isSequential() && ((alignment == LEADING) || (alignment == TRAILING));
482             int index = parent.indexOf(interval);
483             if (alignment == LEADING) {
484                 return (index > 0) ?
485                 parent.getSubInterval(index-1).getCurrentSpace().positions[dimension][TRAILING] :
486                 parent.getCurrentSpace().positions[dimension][LEADING];
487             } else { // alignment == TRAILING
488
return (index+1 < parent.getSubIntervalCount()) ?
489                     parent.getSubInterval(index+1).getCurrentSpace().positions[dimension][LEADING] :
490                     parent.getCurrentSpace().positions[dimension][TRAILING];
491             }
492         } else {
493             return interval.getCurrentSpace().positions[dimension][alignment];
494         }
495     }
496
497     /**
498      * Ensures that the nearest parallel parent of the given intervals is the passed one.
499      *
500      * @param intervals intervals that should be transfered into the given parallel parent.
501      * @param parParent parallel group that is already parent (but maybe not the nearest
502      * parallel parent) of the given intervals.
503      * @param requested alignment.
504      * @return <code>List</code> of intervals (gaps) that should become resizable.
505      */

506     private List transferToParallelParent(LayoutInterval[] intervals, LayoutInterval parParent, int alignment, boolean closed) {
507         // Determine dimension used to align components
508
LayoutComponent temp = intervals[0].getComponent();
509         int dimension = (temp.getLayoutInterval(HORIZONTAL) == intervals[0]) ? HORIZONTAL : VERTICAL;
510
511         // Calculate extreme coordinates
512
int leadingPosition = Short.MAX_VALUE;
513         int trailingPosition = 0;
514         int targetEffAlignment = LayoutConstants.DEFAULT;
515         for (int i=0; i<intervals.length; i++) {
516             LayoutInterval interval = intervals[i];
517             
518             // This method should be called only for components
519
assert interval.isComponent();
520             
521             LayoutRegion region = interval.getCurrentSpace();
522             int leading = region.positions[dimension][LEADING];
523             int trailing = region.positions[dimension][TRAILING];
524             leadingPosition = Math.min(leading, leadingPosition);
525             trailingPosition = Math.max(trailing, trailingPosition);
526             
527             int effAlignment = LayoutInterval.getEffectiveAlignment(interval);
528             if (((effAlignment == LEADING) || (effAlignment == TRAILING))
529                 && ((targetEffAlignment == DEFAULT) || (effAlignment == alignment))) {
530                 targetEffAlignment = effAlignment;
531             }
532         }
533         
534         boolean resizable = false;
535         boolean sequenceResizable;
536         boolean leadingGaps = true;
537         boolean trailingGaps = true;
538         List gapsToResize = new LinkedList();
539         List sequenceGapsToResize;
540         LayoutInterval[] firstIntervals = new LayoutInterval[intervals.length];
541         LayoutInterval[] lastIntervals = new LayoutInterval[intervals.length];
542         
543         // List of new sequence groups for individual intervals
544
List intervalList = Arrays.asList(intervals);
545         List newSequences = new LinkedList();
546         Map gapSizes = new HashMap();
547         for (int i=0; i<intervals.length; i++) {
548             LayoutInterval interval = intervals[i];
549                 
550             // Find intervals that should be in the same sequence with the transfered interval
551
List transferedComponents = transferedComponents(intervals, i, parParent);
552             LayoutInterval firstInterval = null;
553             LayoutInterval lastInterval = null;
554             for (int j = transferedComponents.size()-1; j>=0; j--) {
555                 LayoutInterval trInterval = (LayoutInterval)transferedComponents.get(j);
556                 if (intervalList.contains(trInterval)) {
557                     firstInterval = trInterval;
558                     if (lastInterval == null) {
559                         lastInterval = trInterval;
560                     }
561                 } else if (alignment == CENTER) {
562                     transferedComponents.remove(trInterval);
563                 }
564             }
565             firstIntervals[i] = firstInterval;
566             lastIntervals[i] = lastInterval;
567             
568             // List of LayoutIntervals in the new sequence group
569
List newSequenceList = new LinkedList();
570             newSequences.add(newSequenceList);
571             sequenceResizable = false;
572             sequenceGapsToResize = new LinkedList();
573             Iterator iter = transferedComponents.iterator();
574             
575             // Determine leading gap of the sequence
576
LayoutRegion parentRegion = parParent.getCurrentSpace();
577             LayoutInterval leadingInterval = (LayoutInterval)iter.next();
578             LayoutRegion leadingRegion = leadingInterval.getCurrentSpace();
579             if ((alignment == TRAILING) && !closed) {
580                 int preGap = leadingRegion.positions[dimension][LEADING]
581                     - parentRegion.positions[dimension][LEADING];
582                 LayoutInterval gapInterval = LayoutInterval.getNeighbor(leadingInterval, SEQUENTIAL, LEADING);
583                 leadingGaps = leadingGaps && (preGap != 0);
584                 if ((gapInterval != null) && gapInterval.isEmptySpace() && parParent.isParentOf(gapInterval)
585                     && (LayoutInterval.getIntervalCurrentSize(gapInterval, dimension) == preGap)) {
586                     LayoutInterval gap = cloneGap(gapInterval);
587                     newSequenceList.add(gap);
588                     gapSizes.put(gap, new Integer JavaDoc(preGap));
589                     if (alignment == TRAILING) {
590                         sequenceResizable = sequenceResizable || LayoutInterval.canResize(gap);
591                     }
592                 } else {
593                     maybeAddGap(newSequenceList, preGap, true);
594                     if ((preGap != 0) && (alignment == TRAILING) && (leadingInterval == firstInterval)) {
595                         LayoutInterval gap = (LayoutInterval)newSequenceList.get(newSequenceList.size() - 1);
596                         if (LayoutInterval.getEffectiveAlignment(leadingInterval) == TRAILING) {
597                             layoutModel.setIntervalSize(gap, USE_PREFERRED_SIZE, preGap, USE_PREFERRED_SIZE);
598                             sequenceGapsToResize.add(gap);
599                         }
600                     }
601                 }
602             }
603             
604             // Determine content of the sequence
605
boolean afterDefiningInterval = false;
606             newSequenceList.add(leadingInterval);
607             while (iter.hasNext()) {
608                 if (leadingInterval == interval) {
609                     afterDefiningInterval = true;
610                 }
611                 LayoutInterval trailingInterval = (LayoutInterval)iter.next();
612                 if (((alignment == TRAILING) && (!afterDefiningInterval || (leadingInterval == interval)))
613                     || ((alignment == LEADING) && afterDefiningInterval)) {
614                     sequenceResizable = sequenceResizable || LayoutInterval.canResize(leadingInterval);
615                 }
616                 
617                 // Determine gap between before the processed interval
618
LayoutRegion trailingRegion = trailingInterval.getCurrentSpace();
619                 LayoutInterval gapInterval = LayoutInterval.getNeighbor(leadingInterval, SEQUENTIAL, TRAILING);
620                 int gapSize = trailingRegion.positions[dimension][LEADING]
621                     - leadingRegion.positions[dimension][TRAILING];
622                 boolean gapFound = false;
623                 if (gapInterval.isEmptySpace()) {
624                     LayoutInterval neighbor = LayoutInterval.getNeighbor(gapInterval, SEQUENTIAL, TRAILING);
625                     if (neighbor == trailingInterval) {
626                         gapFound = true;
627                         LayoutInterval gap = cloneGap(gapInterval);
628                         newSequenceList.add(gap);
629                         gapSizes.put(gap, new Integer JavaDoc(gapSize));
630                         if (((alignment == TRAILING) && !afterDefiningInterval)
631                             || ((alignment == LEADING) && afterDefiningInterval)) {
632                             sequenceResizable = sequenceResizable || LayoutInterval.canResize(gap);
633                         }
634                     }
635                 }
636                 if (!gapFound) {
637                     maybeAddGap(newSequenceList, gapSize, (alignment == CENTER));
638                 }
639                 if (((leadingInterval == lastInterval) && (alignment == LEADING)
640                       && (LayoutInterval.getEffectiveAlignment(trailingInterval) == TRAILING))
641                     || ((trailingInterval == firstInterval) && (alignment == TRAILING))
642                       && (LayoutInterval.getEffectiveAlignment(leadingInterval) == LEADING)) {
643                     LayoutInterval gap = (LayoutInterval)newSequenceList.get(newSequenceList.size() - 1);
644                     if (!LayoutInterval.canResize(gap)) {
645                         sequenceGapsToResize.add(gap);
646                     }
647                 }
648                 
649                 newSequenceList.add(trailingInterval);
650                 leadingInterval = trailingInterval;
651                 leadingRegion = trailingRegion;
652             }
653             
654             // Determine trailing gap of the sequence
655
if ((alignment == LEADING) || ((alignment == TRAILING) && (leadingInterval == lastInterval))) {
656                 sequenceResizable = sequenceResizable || LayoutInterval.canResize(leadingInterval);
657             }
658             if ((alignment == LEADING) && !closed) {
659                 int postGap = parentRegion.positions[dimension][TRAILING]
660                     - leadingRegion.positions[dimension][TRAILING];
661                 trailingGaps = trailingGaps && (postGap != 0);
662                 LayoutInterval gapInterval = LayoutInterval.getNeighbor(leadingInterval, SEQUENTIAL, TRAILING);
663                 if ((gapInterval != null) && gapInterval.isEmptySpace() && parParent.isParentOf(gapInterval)
664                     && (LayoutInterval.getIntervalCurrentSize(gapInterval, dimension) == postGap)) {
665                     LayoutInterval gap = cloneGap(gapInterval);
666                     newSequenceList.add(gap);
667                     gapSizes.put(gap, new Integer JavaDoc(postGap));
668                     if (alignment == LEADING) {
669                         sequenceResizable = sequenceResizable || LayoutInterval.canResize(gap);
670                     }
671                 } else {
672                     maybeAddGap(newSequenceList, postGap, true);
673                 }
674             }
675             resizable = resizable || sequenceResizable;
676             if (!sequenceResizable) {
677                 gapsToResize.addAll(sequenceGapsToResize);
678             }
679         }
680         
681         // Modify transfered gaps adjacent to aligned components
682
if (alignment != CENTER) {
683             Iterator listIter = newSequences.iterator();
684             for (int i=0; i<intervals.length; i++) {
685                 List newSequenceList = (List)listIter.next();
686                 Iterator iter = newSequenceList.iterator();
687                 LayoutInterval gapCandidate = null;
688                 while (iter.hasNext()) {
689                     LayoutInterval interval = (LayoutInterval)iter.next();
690                     if (((interval == firstIntervals[i]) && (alignment == LEADING))
691                         || ((interval == lastIntervals[i]) && (alignment == TRAILING))) {
692                         LayoutRegion region = interval.getCurrentSpace();
693                         int diff = 0;
694                         if (alignment == TRAILING) {
695                             if (iter.hasNext()) {
696                                 gapCandidate = (LayoutInterval)iter.next();
697                                 diff = trailingPosition - region.positions[dimension][TRAILING];
698                             } else {
699                                 break;
700                             }
701                         } else {
702                             diff = region.positions[dimension][LEADING] - leadingPosition;
703                         }
704                         if ((gapCandidate != null) && (gapCandidate.isEmptySpace())) {
705                             if ((!leadingGaps && (alignment == LEADING) && (newSequenceList.indexOf(gapCandidate) == 0))
706                                 || (!trailingGaps && (alignment == TRAILING) && !iter.hasNext())) {
707                                 newSequenceList.remove(gapCandidate);
708                             } else {
709                                 Integer JavaDoc size = (Integer JavaDoc)gapSizes.get(gapCandidate);
710                                 int minSize = gapCandidate.getMinimumSize();
711                                 int prefSize = gapCandidate.getPreferredSize();
712                                 int maxSize = gapCandidate.getMaximumSize();
713                                 if (diff > 0) {
714                                     if (size != null) {
715                                         int actualSize = size.intValue();
716                                         diff += prefSize - actualSize;
717                                     }
718                                     if (minSize >= 0) {
719                                         minSize = (minSize - diff > 0) ? minSize - diff : NOT_EXPLICITLY_DEFINED;
720                                     }
721                                     if (prefSize >= 0) {
722                                         prefSize = (prefSize - diff > 0) ? prefSize - diff : NOT_EXPLICITLY_DEFINED;
723                                     }
724                                     if ((maxSize >= 0) && (maxSize != Short.MAX_VALUE)) {
725                                         maxSize = (maxSize - diff > 0) ? maxSize - diff : USE_PREFERRED_SIZE;
726                                     }
727                                 }
728                                 if ((targetEffAlignment == alignment) && (maxSize == Short.MAX_VALUE)) {
729                                     maxSize = USE_PREFERRED_SIZE;
730                                 }
731                                 layoutModel.setIntervalSize(gapCandidate, minSize, prefSize, maxSize);
732                             }
733                         }
734                         break;
735                     }
736                     gapCandidate = interval;
737                 }
738             }
739         }
740
741         // The content of all new sequence groups is known.
742
// We can update the layout model.
743
Iterator listIter = newSequences.iterator();
744         while (listIter.hasNext()) {
745             List newSequenceList = (List)listIter.next();
746             LayoutInterval newSequence = new LayoutInterval(SEQUENTIAL);
747             if (alignment == CENTER) {
748                 newSequence.setAlignment(CENTER);
749             }
750             Iterator iter = newSequenceList.iterator();
751             int sequenceAlignment = DEFAULT;
752             while (iter.hasNext()) {
753                 LayoutInterval compInterval = (LayoutInterval)iter.next();
754                 if (compInterval.isComponent()) { // e.g. compInterval.getParent() != null
755
if (sequenceAlignment == DEFAULT) {
756                         sequenceAlignment = LayoutInterval.getEffectiveAlignment(compInterval);
757                     }
758                     designer.takeOutInterval(compInterval, parParent);
759                     layoutModel.setIntervalAlignment(compInterval, DEFAULT);
760                 }
761                 layoutModel.addInterval(compInterval, newSequence, -1);
762             }
763             if ((alignment != CENTER) && !LayoutInterval.wantResize(newSequence)) {
764                 newSequence.setAlignment(sequenceAlignment);
765             }
766             if (newSequenceList.size() == 1) {
767                 LayoutInterval compInterval = (LayoutInterval)newSequenceList.get(0);
768                 layoutModel.removeInterval(compInterval);
769                 if (newSequence.getAlignment() != DEFAULT) {
770                     layoutModel.setIntervalAlignment(compInterval, newSequence.getAlignment());
771                 }
772                 newSequence = compInterval;
773             }
774             layoutModel.addInterval(newSequence, parParent, -1);
775         }
776         
777         // Check resizability
778
if ((gapsToResize.size() > 0) && !resizable && (alignment != CENTER)) {
779             operations.suppressGroupResizing(parParent);
780             Iterator iter = gapsToResize.iterator();
781             while (iter.hasNext()) {
782                 LayoutInterval gap = (LayoutInterval)iter.next();
783                 layoutModel.changeIntervalAttribute(gap, LayoutInterval.ATTRIBUTE_FORMER_FILL, false);
784                 layoutModel.changeIntervalAttribute(gap, LayoutInterval.ATTRIBUTE_FILL, true);
785             }
786         }
787         return gapsToResize;
788     }
789
790     /** PENDING
791      * Determines layout components that will be transfered to the specified
792      * parallel parent together with the given layout component.
793      *
794      * @param interval layout component to transfer to the parallel parent.
795      * @param parParent parallel parent to transfer the component to.
796      * @return <code>List</code> of <code>LayoutInterval</code> objects.
797      */

798     private List transferedComponents(LayoutInterval[] intervals, int index, LayoutInterval parParent) {
799         LayoutInterval interval = intervals[index];
800         LayoutInterval oppInterval = oppositeComponentInterval(interval);
801         List transferedComponents = new LinkedList();
802         List components = new LinkedList();
803         componentsInGroup(parParent, components);
804         /*for (int i=0; i<intervals.length; i++) {
805             if (i == index) continue;
806             transferCandidates(interval, intervals[i], components);
807         }*/

808         Iterator iter = components.iterator();
809         while (iter.hasNext()) {
810             LayoutInterval candidate = (LayoutInterval)iter.next();
811             LayoutInterval oppCandidate = oppositeComponentInterval(candidate);
812             if (alignedIntervals(oppInterval, oppCandidate, BASELINE)
813                 || alignedIntervals(oppInterval, oppCandidate, LEADING)
814                 || alignedIntervals(oppInterval, oppCandidate, TRAILING)
815                 || alignedIntervals(oppInterval, oppCandidate, CENTER)) {
816                 if (parParent.isParentOf(candidate)) {
817                     transferedComponents.add(candidate);
818                 }
819             }
820         }
821         if (!transferedComponents.contains(interval)) {
822             transferedComponents.add(interval);
823         }
824
825         // Sort layout components according to their current bounds
826
Collections.sort(transferedComponents, new Comparator() {
827             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
828                 LayoutInterval interval1 = (LayoutInterval)o1;
829                 LayoutInterval interval2 = (LayoutInterval)o2;
830                 LayoutComponent comp = interval1.getComponent();
831                 int dimension = (comp.getLayoutInterval(VERTICAL) == interval1)
832                     ? VERTICAL : HORIZONTAL;
833                 LayoutRegion region1 = interval1.getCurrentSpace();
834                 LayoutRegion region2 = interval2.getCurrentSpace();
835                 int value1 = region1.positions[dimension][LEADING];
836                 int value2 = region2.positions[dimension][LEADING];
837                 return (value1 - value2);
838             }
839         });
840         return transferedComponents;
841     }
842
843     /**
844      * Places all components that are in the <code>group</code>
845      * into <code>components</code> collection.
846      *
847      * @param group layout interval that is scanned for components.
848      * @param components collection of <code>LayoutInterval</code>s
849      * of layout components in the group.
850      */

851     private void componentsInGroup(LayoutInterval group, Collection components) {
852         Iterator iter = group.getSubIntervals();
853         while (iter.hasNext()) {
854             LayoutInterval interval = (LayoutInterval)iter.next();
855             if (interval.isGroup()) {
856                 componentsInGroup(interval, components);
857             } else if (interval.isComponent() && !components.contains(interval)) {
858                 components.add(interval);
859             }
860         }
861     }
862
863     /**
864      * Aligns given intervals to a parallel group. The intervals are supposed
865      * to have the same first parallel parent.
866      */

867     private boolean align(LayoutInterval[] leadingInts, LayoutInterval[] trailingInts, boolean closed, int dimension, int alignment) {
868         // find common parallel group for aligned intervals
869
LayoutInterval commonGroup = null;
870         LayoutInterval[] intervals = leadingInts;
871         for (int i=0; i < intervals.length; i++) {
872             LayoutInterval interval = intervals[i];
873             LayoutInterval parent = interval.getParent();
874             if (!parent.isParallel()) {
875                 parent = parent.getParent();
876                 assert parent.isParallel();
877             }
878             if (commonGroup == null || (parent != commonGroup && parent.isParentOf(commonGroup))) {
879                 commonGroup = parent;
880             }
881             else {
882                 assert parent == commonGroup || commonGroup.isParentOf(parent);
883             }
884         }
885
886         // prepare separation to groups
887
List aligned = new LinkedList();
888         List restLeading = new LinkedList();
889         List restTrailing = new LinkedList();
890         int mainEffectiveAlign = -1;
891         int originalCount = commonGroup.getSubIntervalCount();
892
893         for (int i=0; i < intervals.length; i++) {
894             LayoutInterval interval = intervals[i];
895             LayoutInterval parent = interval.getParent();
896             LayoutInterval parParent = parent.isParallel() ? parent : parent.getParent();
897             if (parParent != commonGroup) {
898                 interval = getAlignSubstitute(interval, commonGroup, alignment);
899                 if (interval == null) {
900                     return false; // cannot align
901
}
902                 parent = interval.getParent();
903             }
904
905             if (parent.isSequential()) {
906                 mainEffectiveAlign = LayoutInterval.getEffectiveAlignment(interval); // [need better way to collect - here it takes the last one...]
907

908                 // extract the interval surroundings
909
int extractCount = operations.extract(interval, closed ? trailingInts[i] : interval, alignment, closed,
910                                                       restLeading, restTrailing);
911                 if (extractCount == 1) { // the parent won't be reused
912
layoutModel.removeInterval(parent);
913                     aligned.add(interval);
914                 }
915                 else { // we'll reuse the parent sequence in the new group
916
aligned.add(parent);
917                 }
918             }
919             else {
920                 aligned.add(interval);
921             }
922         }
923
924         // prepare the group where the aligned intervals will be placed
925
LayoutInterval group;
926         LayoutInterval commonSeq;
927         boolean remainder = !restLeading.isEmpty() || !restTrailing.isEmpty();
928
929         if ((!remainder && mainEffectiveAlign == alignment)
930             || (aligned.size() == originalCount
931                 && commonGroup.getParent() != null))
932         { // reuse the original group - avoid unnecessary nesting
933
group = commonGroup;
934             if (remainder) { // need a sequence for the remainder groups
935
LayoutInterval groupParent = group.getParent();
936                 if (groupParent.isSequential()) {
937                     commonSeq = groupParent;
938                 }
939                 else { // insert a new one
940
int index = layoutModel.removeInterval(group);
941                     commonSeq = new LayoutInterval(SEQUENTIAL);
942                     commonSeq.setAlignment(group.getAlignment());
943                     layoutModel.addInterval(commonSeq, groupParent, index);
944                     layoutModel.setIntervalAlignment(group, DEFAULT);
945                     layoutModel.addInterval(group, commonSeq, -1);
946                 }
947             }
948             else commonSeq = null;
949         }
950         else { // need to create a new group
951
group = new LayoutInterval(PARALLEL);
952             if (remainder) { // need a new sequence for the remainder groups
953
commonSeq = new LayoutInterval(SEQUENTIAL);
954                 commonSeq.add(group, -1);
955                 layoutModel.addInterval(commonSeq, commonGroup, -1);
956             }
957             else {
958                 commonSeq = null;
959                 layoutModel.addInterval(group, commonGroup, -1);
960             }
961             layoutModel.setGroupAlignment(group, alignment);
962         }
963
964         // add the intervals and their neighbors to the main aligned group
965
// [need to fix the resizability (fill) and compute effective alignment]
966
for (Iterator it=aligned.iterator(); it.hasNext(); ) {
967             LayoutInterval interval = (LayoutInterval) it.next();
968             if (interval.getParent() != group) {
969                 layoutModel.removeInterval(interval);
970                 operations.addContent(interval, group, -1);
971             }
972             layoutModel.setIntervalAlignment(interval, alignment);
973         }
974
975         // create the remainder groups around the main one
976
if (!restLeading.isEmpty()) {
977             // [should change to operations.addGroupContent]
978
designer.createRemainderGroup(restLeading, commonSeq, commonSeq.indexOf(group), LEADING, mainEffectiveAlign, dimension);
979         }
980         if (!restTrailing.isEmpty()) {
981             // [should change to operations.addGroupContent]
982
designer.createRemainderGroup(restTrailing, commonSeq, commonSeq.indexOf(group), TRAILING, mainEffectiveAlign, dimension);
983         }
984
985         return true;
986     }
987
988     /**
989      * Clones given layout interval (empty space).
990      *
991      * @return clone of the given layout interval (empty space).
992      */

993     private LayoutInterval cloneGap(LayoutInterval interval) {
994         assert interval.isEmptySpace();
995         LayoutInterval gap = new LayoutInterval(SINGLE);
996         gap.setMinimumSize(interval.getMinimumSize());
997         gap.setPreferredSize(interval.getPreferredSize());
998         gap.setMaximumSize(interval.getMaximumSize());
999         return gap;
1000    }
1001
1002    /**
1003     * Helper method that adds layout interval (empty space) to the given
1004     * list (when the specified size is positive).
1005     *
1006     * @param list list the gap should be added to.
1007     * @param size size of the original space.
1008     */

1009    private void maybeAddGap(List list, int size, boolean forceSize) {
1010        if (size > 0) {
1011            LayoutInterval gapInterval = new LayoutInterval(SINGLE);
1012            if (forceSize) {
1013                layoutModel.setIntervalSize(gapInterval, size, size, size);
1014            }
1015            list.add(gapInterval);
1016        }
1017    }
1018
1019    private static boolean compatibleGroupAlignment(int groupAlign, int align) {
1020        return groupAlign == align
1021               || ((groupAlign == LEADING || groupAlign == TRAILING)
1022                   && (align == LEADING || align == TRAILING || align == DEFAULT));
1023    }
1024
1025    private static boolean alignedIntervals(LayoutInterval interval1, LayoutInterval interval2, int alignment) {
1026        LayoutInterval commonParent;
1027        LayoutInterval otherInterval;
1028        if (interval1.isParentOf(interval2)) {
1029            commonParent = interval1;
1030            otherInterval = interval2;
1031        }
1032        else if (interval2.isParentOf(interval1)) {
1033            commonParent = interval2;
1034            otherInterval = interval1;
1035        }
1036        else {
1037            commonParent = interval1.getParent();
1038            while (commonParent != null) {
1039                if (!hasAlignmentInParent(interval1, alignment)) {
1040                    return false;
1041                }
1042                if (commonParent.isParentOf(interval2)) {
1043                    break;
1044                }
1045                interval1 = commonParent;
1046                commonParent = interval1.getParent();
1047            }
1048            if (commonParent == null) {
1049                return false;
1050            }
1051            otherInterval = interval2;
1052        }
1053
1054        do {
1055            if (!hasAlignmentInParent(otherInterval, alignment)) {
1056                return false;
1057            }
1058            otherInterval = otherInterval.getParent();
1059        }
1060        while (otherInterval != commonParent);
1061        return true;
1062    }
1063
1064    private static boolean hasAlignmentInParent(LayoutInterval interval, int alignment) {
1065        LayoutInterval parent = interval.getParent();
1066        if (parent.isSequential()) {
1067            if (alignment == LEADING) {
1068                return parent.getSubInterval(0) == interval;
1069            }
1070            if (alignment == TRAILING) {
1071                return parent.getSubInterval(parent.getSubIntervalCount()-1) == interval;
1072            }
1073            return false;
1074        }
1075        else { // parallel group
1076
assert interval.getAlignment() != alignment || compatibleGroupAlignment(parent.getGroupAlignment(), alignment);
1077            return interval.getAlignment() == alignment
1078                   || LayoutInterval.wantResize(interval);
1079        }
1080    }
1081
1082    private static LayoutInterval getAlignSubstitute(LayoutInterval toAlignWith, LayoutInterval commonParParent, int alignment) {
1083        assert alignment == LEADING || alignment == TRAILING;
1084
1085        while (toAlignWith != null && LayoutInterval.getFirstParent(toAlignWith, PARALLEL) != commonParParent) {
1086            if (LayoutInterval.isAlignedAtBorder(toAlignWith, alignment)) {
1087                toAlignWith = toAlignWith.getParent();
1088            }
1089            else return null;
1090        }
1091        return toAlignWith;
1092    }
1093
1094    /**
1095     * Returns layout interval for the opposite dimension for the given
1096     * layout interval of a layout component.
1097     *
1098     * @param interval layout interval of some layout component.
1099     * @return layout interval for the opposite dimension for the given
1100     * layout interval of a layout component.
1101     */

1102    private LayoutInterval oppositeComponentInterval(LayoutInterval interval) {
1103        assert interval.isComponent();
1104        LayoutComponent component = interval.getComponent();
1105        int oppDimension = (component.getLayoutInterval(HORIZONTAL) == interval)
1106            ? VERTICAL : HORIZONTAL;
1107        return component.getLayoutInterval(oppDimension);
1108    }
1109
1110    private int shortenGap(LayoutInterval gap, int delta) {
1111        assert gap.isEmptySpace();
1112        int prefSize = gap.getPreferredSize();
1113        if (prefSize == NOT_EXPLICITLY_DEFINED) {
1114            return 0; // don't shorten paddings
1115
} else {
1116            int newPref = prefSize - delta;
1117            newPref = (newPref > 0) ? newPref : NOT_EXPLICITLY_DEFINED; // Use padding
1118
if (LayoutInterval.canResize(gap)) {
1119                layoutModel.setIntervalSize(gap, NOT_EXPLICITLY_DEFINED, newPref, Short.MAX_VALUE);
1120            } else {
1121                layoutModel.setIntervalSize(gap, USE_PREFERRED_SIZE, newPref, USE_PREFERRED_SIZE);
1122            }
1123            return (prefSize > delta) ? delta : prefSize;
1124        }
1125    }
1126
1127    private void returnRemovedIntervals(LayoutInterval parParent, List removed, int dimension) {
1128        LayoutRegion parRegion = parParent.getCurrentSpace();
1129        Iterator iter = removed.iterator();
1130        while (iter.hasNext()) {
1131            LayoutInterval interval = (LayoutInterval)iter.next();
1132            LayoutRegion region = interval.getCurrentSpace();
1133            int pre = Math.max(0, region.positions[dimension][LEADING] - parRegion.positions[dimension][LEADING]);
1134            int post = Math.max(0, parRegion.positions[dimension][TRAILING] - region.positions[dimension][TRAILING]);
1135            if (((pre != 0) || (post != 0)) && !interval.isSequential()) {
1136                LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
1137                if (interval.getAlignment() != BASELINE)
1138                    layoutModel.setIntervalAlignment(seq, interval.getAlignment());
1139                layoutModel.setIntervalAlignment(interval, DEFAULT);
1140                layoutModel.addInterval(interval, seq, -1);
1141                interval = seq;
1142            }
1143            
1144            // interval.isSequence() by now - remove boundary empty spaces
1145
if (pre != 0) {
1146                LayoutInterval first = interval.getSubInterval(0);
1147                if (first.isEmptySpace()) {
1148                    layoutModel.removeInterval(first);
1149                    region = interval.getSubInterval(0).getCurrentSpace();
1150                    pre = Math.max(0, region.positions[dimension][LEADING] - parRegion.positions[dimension][LEADING]);
1151                }
1152            }
1153            if (post != 0) {
1154                LayoutInterval last = interval.getSubInterval(interval.getSubIntervalCount()-1);
1155                if (last.isEmptySpace()) {
1156                    layoutModel.removeInterval(last);
1157                    region = interval.getSubInterval(interval.getSubIntervalCount()-1).getCurrentSpace();
1158                    post = Math.max(0, parRegion.positions[dimension][TRAILING] - region.positions[dimension][TRAILING]);
1159                }
1160            }
1161            
1162            // Insert new empty spaces
1163
if (pre != 0) {
1164                LayoutInterval gap = new LayoutInterval(SINGLE);
1165                gap.setSize(pre);
1166                if (interval.getAlignment() == TRAILING) designer.setIntervalResizing(gap, true);
1167                layoutModel.addInterval(gap, interval, 0);
1168            }
1169            if (post != 0) {
1170                LayoutInterval gap = new LayoutInterval(SINGLE);
1171                gap.setSize(post);
1172                if (interval.getAlignment() == LEADING) designer.setIntervalResizing(gap, true);
1173                layoutModel.addInterval(gap, interval, -1);
1174            }
1175            
1176            // Insert into parParent
1177
layoutModel.addInterval(interval, parParent, -1);
1178        }
1179    }
1180
1181    private void setAlignmentAccordingEffectiveAlignment(LayoutInterval aligned, LayoutInterval interval) {
1182        if (interval == null) return;
1183        int alignment = LayoutInterval.getEffectiveAlignment(interval);
1184        if ((alignment == LEADING) || (alignment == TRAILING)) {
1185            layoutModel.setIntervalAlignment(aligned, alignment);
1186        }
1187    }
1188
1189}
Popular Tags