KickJava   Java API By Example, From Geeks To Geeks.

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


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  * This class serves as a library of various useful and well-defined operations
26  * on the layout model.
27  *
28  * @author Tomas Pavek
29  */

30
31 class LayoutOperations implements LayoutConstants {
32
33     private LayoutModel layoutModel;
34
35     private VisualMapper visualMapper;
36
37     LayoutOperations(LayoutModel model, VisualMapper mapper) {
38         layoutModel = model;
39         visualMapper = mapper;
40     }
41
42     LayoutModel getModel() {
43         return layoutModel;
44     }
45
46     VisualMapper getMapper() {
47         return visualMapper;
48     }
49
50     // -----
51

52     /**
53      * Extracts surroundings of given interval (placed in a sequential group).
54      * Extracted intervals are removed and go to the 'restLeading' and
55      * 'restTrailing' lists. Does not extract/remove the interval itself.
56      */

57     int extract(LayoutInterval interval, int alignment, boolean closed,
58                 List restLeading, List restTrailing) {
59         return extract(interval, interval, alignment, closed, restLeading, restTrailing);
60     }
61         
62     int extract(LayoutInterval leading, LayoutInterval trailing, int alignment, boolean closed,
63                 List restLeading, List restTrailing)
64     {
65         LayoutInterval seq = leading.getParent();
66         assert seq.isSequential();
67
68         int leadingIndex = seq.indexOf(leading);
69         int trailingIndex = seq.indexOf(trailing);
70         int count = seq.getSubIntervalCount();
71         int extractCount;
72         if (closed) {
73             extractCount = trailingIndex - leadingIndex + 1;
74         } else if (alignment != LEADING && alignment != TRAILING) {
75             extractCount = 1;
76         }
77         else {
78             extractCount = alignment == LEADING ? count - leadingIndex : leadingIndex + 1;
79         }
80
81         if (extractCount < seq.getSubIntervalCount()) {
82             List toRemainL = null;
83             List toRemainT = null;
84             int startIndex = alignment == LEADING ? leadingIndex : leadingIndex - extractCount + 1;
85             int endIndex = alignment == LEADING ? trailingIndex + extractCount - 1 : trailingIndex;
86             Iterator it = seq.getSubIntervals();
87             for (int idx=0; it.hasNext(); idx++) {
88                 LayoutInterval li = (LayoutInterval) it.next();
89                 if (idx < startIndex) {
90                     if (toRemainL == null) {
91                         toRemainL = new LinkedList();
92                         toRemainL.add(new Integer JavaDoc(LayoutInterval.getEffectiveAlignment(li)));
93                     }
94                     toRemainL.add(li);
95                 }
96                 else if (idx > endIndex) {
97                     if (toRemainT == null) {
98                         toRemainT = new LinkedList();
99                         toRemainT.add(new Integer JavaDoc(LayoutInterval.getEffectiveAlignment(li)));
100                     }
101                     toRemainT.add(li);
102                 }
103             }
104             if (toRemainL != null) {
105                 it = toRemainL.iterator();
106                 it.next();
107                 do {
108                     layoutModel.removeInterval((LayoutInterval)it.next());
109                 }
110                 while (it.hasNext());
111                 restLeading.add(toRemainL);
112             }
113             if (toRemainT != null) {
114                 it = toRemainT.iterator();
115                 it.next();
116                 do {
117                     layoutModel.removeInterval((LayoutInterval)it.next());
118                 }
119                 while (it.hasNext());
120                 restTrailing.add(toRemainT);
121             }
122         }
123
124         return extractCount;
125     }
126
127     /**
128      * Adds parallel content of a group specified in List to given sequence.
129      * Used to create a remainder parallel group to a group of aligned intervals.
130      * @param list the content of the group, output from 'extract' method
131      * @param seq a sequential group where to add to
132      * @param index the index in the sequence where to add
133      * @param dimension
134      * @param position the position of the remainder group relative to the main
135      * group (LEADING or TRAILING)
136 // * @param mainAlignment effective alignment of the main group (LEADING or
137 // * TRAILING or something else meaning not aligned)
138      * @return parallel group if it has been created, or null
139      */

140     LayoutInterval addGroupContent(List list, LayoutInterval seq,
141                                    int index, int dimension, int position/*, int mainAlignment*/)
142     {
143         assert seq.isSequential() && (position == LEADING || position == TRAILING);
144         boolean resizingFillGap = false;
145         LayoutInterval commonGap = null;
146         boolean onlyGaps = true;
147
148         // Remove sequences just with one gap
149
for (int i=list.size()-1; i >= 0; i--) {
150             List subList = (List)list.get(i);
151             assert subList.size() >= 2;
152             if (subList.size() == 2) { // there is just one interval
153
LayoutInterval li = (LayoutInterval) subList.get(1);
154                 if (li.isEmptySpace()) {
155                     if (commonGap == null || li.getPreferredSize() > commonGap.getPreferredSize())
156                         commonGap = li;
157                     if (LayoutInterval.canResize(li))
158                         resizingFillGap = true;
159                     list.remove(i);
160                 }
161                 else onlyGaps = false;
162             }
163             else onlyGaps = false;
164         }
165
166         if (onlyGaps) { // just one gap
167
if (resizingFillGap && !LayoutInterval.canResize(commonGap))
168                 layoutModel.setIntervalSize(commonGap, NOT_EXPLICITLY_DEFINED,
169                                                        commonGap.getPreferredSize(),
170                                                        Short.MAX_VALUE);
171             insertGapIntoSequence(commonGap, seq, index, dimension);
172             return null;
173         }
174
175         if (list.size() == 1) { // just one sequence
176
List subList = (List) list.get(0);
177             for (int n=subList.size(),i=n-1; i > 0; i--) { // skip alignment at 0
178
LayoutInterval li = (LayoutInterval) subList.get(i);
179                 if (resizingFillGap && li.isEmptySpace() && !LayoutInterval.canResize(li)
180                     && ((i == 1 && position == TRAILING) || (i == n-1 && position == LEADING)))
181                 { // make the end gap resizing
182
layoutModel.setIntervalSize(
183                             li, NOT_EXPLICITLY_DEFINED, li.getPreferredSize(), Short.MAX_VALUE);
184                 }
185                 if (i == 1 && li.isEmptySpace()) // first gap
186
insertGapIntoSequence(li, seq, index, dimension);
187                 else
188                     layoutModel.addInterval(li, seq, index);
189             }
190             return null;
191         }
192
193         // create parallel group for multiple intervals/sequences
194
LayoutInterval group = new LayoutInterval(PARALLEL);
195 // if (position == mainAlignment) {
196
// // [but this should eliminate resizability only for gaps...]
197
// group.setMinimumSize(USE_PREFERRED_SIZE);
198
// group.setMaximumSize(USE_PREFERRED_SIZE);
199
// }
200
//// group.setGroupAlignment(alignment);
201

202         // fill the group
203
for (Iterator it=list.iterator(); it.hasNext(); ) {
204             List subList = (List) it.next();
205             LayoutInterval interval;
206             if (subList.size() == 2) { // there is just one interval - use it directly
207
int alignment = ((Integer JavaDoc)subList.get(0)).intValue();
208                 interval = (LayoutInterval) subList.get(1);
209                 if (alignment == LEADING || alignment == TRAILING)
210                     layoutModel.setIntervalAlignment(interval, alignment);
211             }
212             else { // there are more intervals - create sequence
213
interval = new LayoutInterval(SEQUENTIAL);
214                 int alignment = ((Integer JavaDoc)subList.get(0)).intValue();
215                 if (alignment == LEADING || alignment == TRAILING)
216                     interval.setAlignment(alignment);
217                 for (int i=1,n=subList.size(); i < n; i++) {
218                     LayoutInterval li = (LayoutInterval) subList.get(i);
219                     if (resizingFillGap && li.isEmptySpace() && !LayoutInterval.canResize(li)
220                         && ((i == 1 && position == TRAILING) || (i == n-1 && position == LEADING)))
221                     { // make the end gap resizing
222
layoutModel.setIntervalSize(
223                                 li, NOT_EXPLICITLY_DEFINED, li.getPreferredSize(), Short.MAX_VALUE);
224                     }
225                     layoutModel.addInterval(li, interval, -1);
226                 }
227             }
228             layoutModel.addInterval(interval, group, -1);
229         }
230
231         layoutModel.addInterval(group, seq, index);
232
233         return group;
234     }
235
236     /**
237      * Adds 'interval' to 'target'. In case of 'interval' is a group, it is
238      * dismounted to individual intervals if needed (e.g. if adding sequence to
239      * sequence), or if producing equal result with less nesting (e.g. when
240      * adding parallel group to parallel group with same alignment).
241      * Also redundant groups are canceled (containing just one interval).
242      */

243     boolean addContent(LayoutInterval interval, LayoutInterval target, int index) {
244         if (interval.isGroup() && interval.getSubIntervalCount() == 1) {
245             return addContent(layoutModel.removeInterval(interval, 0), target, index);
246         }
247
248         if (interval.isSequential() && target.isSequential()) {
249             if (index < 0) {
250                 index = target.getSubIntervalCount();
251             }
252             while (interval.getSubIntervalCount() > 0) {
253                 LayoutInterval li = layoutModel.removeInterval(interval, 0);
254                 layoutModel.addInterval(li, target, index++);
255             }
256             return true;
257         }
258         else if (interval.isParallel() && target.isParallel()) {
259             int align = interval.getAlignment();
260             if (align == DEFAULT) {
261                 align = target.getGroupAlignment();
262             }
263             boolean sameAlign = true;
264             Iterator it = interval.getSubIntervals();
265             while (it.hasNext()) {
266                 LayoutInterval li = (LayoutInterval) it.next();
267                 if (LayoutInterval.wantResize(li)) { // will span over whole target group
268
sameAlign = true;
269                     break;
270                 }
271                 if (li.getAlignment() != align) {
272                     sameAlign = false;
273                 }
274             }
275
276             if (sameAlign
277                 && (LayoutInterval.canResize(interval) || !LayoutInterval.canResize(target) || !LayoutInterval.wantResize(target)))
278             { // can dismantle the group
279
assert interval.getParent() == null;
280                 while (interval.getSubIntervalCount() > 0) {
281                     LayoutInterval li = interval.getSubInterval(0);
282                     if (li.getRawAlignment() == DEFAULT
283                         && interval.getGroupAlignment() != target.getGroupAlignment())
284                     { // force alignment explicitly
285
layoutModel.setIntervalAlignment(li, li.getAlignment());
286                     }
287                     layoutModel.removeInterval(li);
288                     layoutModel.addInterval(li, target, index);
289                     if (index >= 0)
290                         index++;
291                 }
292                 if (!LayoutInterval.canResize(interval) && LayoutInterval.canResize(target)) {
293                     suppressGroupResizing(target);
294                 }
295                 return true;
296             }
297             else { // need to add the group as a whole
298
layoutModel.addInterval(interval, target, index);
299             }
300         }
301         else {
302             if (target.isSequential() && interval.getRawAlignment() != DEFAULT) {
303                 layoutModel.setIntervalAlignment(interval, DEFAULT);
304             }
305             layoutModel.addInterval(interval, target, index);
306         }
307         return false;
308     }
309
310     void resizeInterval(LayoutInterval interval, int size) {
311         assert size >= 0 || size == NOT_EXPLICITLY_DEFINED;
312         int min = (interval.getMinimumSize() == interval.getPreferredSize()
313                    && interval.getMaximumSize() < Short.MAX_VALUE) ?
314                   size : interval.getMinimumSize();
315         int max = interval.getMaximumSize() == interval.getPreferredSize() ?
316                   ((size == NOT_EXPLICITLY_DEFINED) ? USE_PREFERRED_SIZE : size) : interval.getMaximumSize();
317         layoutModel.setIntervalSize(interval, min, size, max);
318     }
319
320     void suppressGroupResizing(LayoutInterval group) {
321         // don't for root group
322
if (group.getParent() != null) {
323             layoutModel.setIntervalSize(group, group.getMinimumSize(),
324                                                group.getPreferredSize(),
325                                                USE_PREFERRED_SIZE);
326         }
327     }
328
329     void enableGroupResizing(LayoutInterval group) {
330         layoutModel.setIntervalSize(group, group.getMinimumSize(),
331                                            group.getPreferredSize(),
332                                            NOT_EXPLICITLY_DEFINED);
333     }
334
335     void mergeParallelGroups(LayoutInterval group) {
336         assert group.isParallel();
337         if (!group.isParallel())
338             return;
339
340         for (int i=group.getSubIntervalCount()-1; i >= 0; i--) {
341             LayoutInterval sub = group.getSubInterval(i);
342             if (sub.isParallel()) {
343                 mergeParallelGroups(sub);
344                 dissolveRedundantGroup(sub);
345             }
346         }
347     }
348
349     /**
350      * Dissolves given group to parent group in case it is redundant.
351      * @return true if the group was dissolved
352      */

353     boolean dissolveRedundantGroup(LayoutInterval group) {
354         LayoutInterval parent = group.getParent();
355         if (parent == null)
356             return false;
357
358         boolean dissolve = false;
359         if (group.getSubIntervalCount() == 1) {
360             dissolve = true;
361         }
362         else if (group.isSequential() && parent.isSequential()) {
363             dissolve = true;
364         }
365         else if (group.isParallel() && parent.isParallel()) {
366             // check for compatible alignment and resizability
367
int align = group.getAlignment();
368             boolean sameAlign = true;
369             boolean subResizing = false;
370             Iterator it = group.getSubIntervals();
371             while (it.hasNext()) {
372                 LayoutInterval li = (LayoutInterval) it.next();
373                 if (!subResizing && LayoutInterval.wantResize(li)) {
374                     subResizing = true;
375                 }
376                 if (li.getAlignment() != align && group.getSubIntervalCount() > 1) {
377                     sameAlign = false;
378                 }
379             }
380             boolean compatible;
381             if (subResizing && (sameAlign || group.getGroupAlignment() != BASELINE)) {
382                 compatible = false;
383                 if (LayoutInterval.canResize(group) || !LayoutInterval.canResize(parent)) {
384                     it = parent.getSubIntervals();
385                     while (it.hasNext()) {
386                         LayoutInterval li = (LayoutInterval) it.next();
387                         if (li != group && LayoutInterval.wantResize(li)) {
388                             compatible = true;
389                             break;
390                         }
391                     }
392                     if (!compatible) {
393                         LayoutInterval neighbor = LayoutInterval.getNeighbor(
394                                 parent, group.getAlignment()^1, false, true, true);
395                         if (neighbor != null && neighbor.isEmptySpace()
396                             && neighbor.getPreferredSize() == NOT_EXPLICITLY_DEFINED)
397                         { // default fixed padding means there is no space for
398
// independent size change, so the subgroup can be merged
399
compatible = true;
400                         }
401                     }
402                 }
403             }
404             else compatible = sameAlign;
405
406             dissolve = compatible;
407         }
408
409         if (dissolve) { // the sub-group can be dissolved into parent group
410
int index = layoutModel.removeInterval(group);
411             while (group.getSubIntervalCount() > 0) {
412                 LayoutInterval li = group.getSubInterval(0);
413                 if (parent.isParallel()) { // moving to parallel group
414
if (group.isParallel()) { // from parallel group
415
if (li.getRawAlignment() == DEFAULT
416                             && group.getGroupAlignment() != parent.getGroupAlignment())
417                         { // force alignment explicitly
418
layoutModel.setIntervalAlignment(li, li.getAlignment());
419                         }
420                     }
421                     else { // from sequential group
422
layoutModel.setIntervalAlignment(li, group.getRawAlignment());
423                     }
424                 }
425                 else { // moving to sequential group
426
if (li.getRawAlignment() != DEFAULT)
427                         layoutModel.setIntervalAlignment(li, DEFAULT);
428                 }
429                 layoutModel.removeInterval(li);
430                 layoutModel.addInterval(li, parent, index++);
431             }
432             return true;
433         }
434         return false;
435     }
436
437     /**
438      * This method goes through a sequential group and moves each interval next
439      * to an open edge of a parallel group into the group.
440      * @param parent sequential group to process
441      * @param dimension
442      */

443     void moveInsideSequential(LayoutInterval parent, int dimension) {
444         assert parent.isSequential();
445         if (!parent.isSequential())
446             return;
447
448         int alignment = LEADING;
449         do {
450             LayoutInterval extend = findIntervalToExtend(parent, dimension, alignment);
451             if (extend == null) {
452                 if (alignment == LEADING) {
453                     alignment = TRAILING;
454                     extend = findIntervalToExtend(parent, dimension, alignment);
455                 }
456                 if (extend == null)
457                     break;
458             }
459
460             LayoutInterval inGroup = extend.getParent(); // group to infiltrate
461
LayoutInterval outGroup = inGroup;
462             while (outGroup.getParent() != parent) {
463                 outGroup = outGroup.getParent();
464             }
465             int index = parent.indexOf(outGroup);
466             int d = alignment == LEADING ? -1 : 1;
467
468             // will the group remain open at the opposite edge?
469
boolean commonEndingGap = true;
470             for (int i=index-d, n=parent.getSubIntervalCount(); i >= 0 && i < n; i-=d) {
471                 LayoutInterval li = parent.getSubInterval(i);
472                 if ((!li.isEmptySpace() || (i-d >= 0 && i-d < n)) // ignore last gap
473
&& LayoutInterval.wantResize(li))
474                 { // resizing interval will close the group
475
// possibly need to separate the rest of the group not to be influenced
476
LayoutInterval endGap = parent.getSubInterval(alignment == LEADING ? n-1 : 0);
477                     if (endGap == null || endGap.getPreferredSize() != NOT_EXPLICITLY_DEFINED) {
478                         commonEndingGap = false;
479                         LayoutInterval closing = extend;
480                         int borderPos = parent.getCurrentSpace().positions[dimension][alignment^1];
481                         do {
482                             LayoutInterval par = closing.getParent();
483                             if (par.isParallel()) {
484                                 separateGroupContent(closing, borderPos, dimension, alignment^1);
485                             }
486                             closing = par;
487                         }
488                         while (closing != outGroup);
489                     }
490                     break;
491                 }
492             }
493
494             int extendPos = extend.getCurrentSpace().positions[dimension][alignment^1];
495             if (!extend.isSequential()) {
496                 LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
497                 seq.setAlignment(extend.getAlignment());
498                 layoutModel.addInterval(seq, inGroup, layoutModel.removeInterval(extend));
499                 layoutModel.setIntervalAlignment(extend, DEFAULT);
500                 layoutModel.addInterval(extend, seq, 0);
501                 extend = seq;
502             }
503
504             // move the intervals from outside inside the group, next to found interval (extend)
505
LayoutInterval connectingGap = null;
506             int idx, addIdx;
507             if (alignment == LEADING) {
508                 idx = index + 1; // start behind the group
509
addIdx = extend.getSubIntervalCount(); // add behind the interval
510
}
511             else {
512                 idx = index - 1; // start before the group
513
addIdx = 0; // add before the interval
514
}
515             while (idx >= 0 && idx < parent.getSubIntervalCount()) {
516                 LayoutInterval li = parent.getSubInterval(idx);
517                 if (li.isEmptySpace()) {
518                     if (connectingGap == null) { // first gap
519
if (extendPos != outGroup.getCurrentSpace().positions[dimension][alignment^1]) {
520                             // need to extend the first gap (extended interval inside group is smaller than the group)
521
int neighborPos = parent.getSubInterval(idx-d).getCurrentSpace().positions[dimension][alignment];
522                             int distance = d * (extendPos - neighborPos);
523                             if (distance > 0)
524                                 resizeInterval(li, distance);
525                         }
526                         connectingGap = li;
527                     }
528                     else if ((idx == 0 || idx == parent.getSubIntervalCount()-1)
529                              && commonEndingGap)
530                     { // keep the last gap out
531
break;
532                     }
533                 }
534                 layoutModel.removeInterval(li);
535                 layoutModel.addInterval(li, extend, addIdx);
536                 if (alignment == LEADING)
537                     addIdx++;
538                 else
539                     idx--;
540             }
541
542             // check if the sequence was not whole moved into the group
543
if (parent.getSubIntervalCount() == 1) { // only neighborGroup remained, eliminate the parent group
544
assert outGroup == parent.getSubInterval(0);
545                 layoutModel.removeInterval(outGroup);
546                 LayoutInterval superParent = parent.getParent();
547                 addContent(outGroup, superParent, layoutModel.removeInterval(parent));
548                 break;
549             }
550         }
551         while (true);
552     }
553
554     private LayoutInterval findIntervalToExtend(LayoutInterval parent, int dimension, int alignment) {
555         int d = alignment == LEADING ? -1 : 1;
556         int count = parent.getSubIntervalCount();
557         int idx = alignment == LEADING ? count-1 : 0;
558         boolean atBorder = true;
559         boolean gap = false;
560
561         while (idx >= 0 && idx < parent.getSubIntervalCount()) {
562             LayoutInterval sub = parent.getSubInterval(idx);
563             if (sub.isEmptySpace()) {
564                 gap = true;
565             }
566             else {
567                 if (!atBorder && gap && sub.isParallel()
568                     && !LayoutInterval.isClosedGroup(sub, alignment^1))
569                 { // this open parallel sub-group might be a candidate to move inside to
570
int startIndex, endIndex;
571                     if (alignment == LEADING) {
572                         startIndex = idx + 1;
573                         endIndex = parent.getSubIntervalCount() - 1;
574                     }
575                     else {
576                         startIndex = 0;
577                         endIndex = idx - 1;
578                     }
579                     LayoutInterval extend = prepareGroupExtension(
580                             sub, parent, startIndex, endIndex, dimension, alignment^1);
581                     if (extend != null)
582                         return extend;
583                 }
584                 gap = false;
585                 atBorder = false;
586             }
587             idx += d;
588         }
589         return null;
590     }
591
592     private LayoutInterval prepareGroupExtension(LayoutInterval group,
593                     LayoutInterval parent, int startIndex, int endIndex,
594                     int dimension, int alignment)
595     {
596         boolean allOverlapping = true;
597         LayoutInterval singleOverlap = null;
598         List overlapList = null;
599
600         // looking for all intervals the given space is located next to
601
Iterator it = group.getSubIntervals();
602         while (it.hasNext()) {
603             LayoutInterval li = (LayoutInterval) it.next();
604             if (!li.isEmptySpace()) {
605                 if (LayoutUtils.contentOverlap(li, parent, startIndex, endIndex, dimension^1)) {
606                     // interval overlaps orthogonally
607
if (singleOverlap == null) {
608                         singleOverlap = li;
609                     }
610                     else {
611                         if (overlapList == null) {
612                             overlapList = new LinkedList();
613                             overlapList.add(singleOverlap);
614                         }
615                         overlapList.add(li);
616                     }
617                 }
618                 else allOverlapping = false;
619             }
620         }
621
622         if (allOverlapping || singleOverlap == null)
623             return null; // spans whole group or nothing
624

625         if (overlapList != null) { // overlaps multiple intervals
626
LayoutInterval subGroup = new LayoutInterval(PARALLEL);
627             subGroup.setGroupAlignment(alignment^1);
628             subGroup.setAlignment(alignment^1);
629             int index = -1;
630             do {
631                 LayoutInterval li = (LayoutInterval) overlapList.remove(0);
632                 int idx = layoutModel.removeInterval(li);
633                 if (index < 0) {
634                     index = idx;
635                 }
636                 layoutModel.addInterval(li, subGroup, -1);
637                 subGroup.getCurrentSpace().expand(li.getCurrentSpace());
638             }
639             while (overlapList.size() > 0);
640
641             layoutModel.addInterval(subGroup, group, index);
642             singleOverlap = subGroup;
643         }
644         else {
645             LayoutInterval subParallel;
646             if (singleOverlap.isSequential()) {
647                 subParallel = singleOverlap.getSubInterval(
648                               alignment == LEADING ? 0 : singleOverlap.getSubIntervalCount()-1);
649                 if (!subParallel.isParallel())
650                     subParallel = null;
651             }
652             else if (singleOverlap.isParallel()) {
653                 subParallel = singleOverlap;
654             }
655             else subParallel = null;
656
657             if (subParallel != null && !LayoutInterval.isClosedGroup(subParallel, alignment)) {
658                 LayoutInterval subOverlap = prepareGroupExtension(
659                         subParallel, parent, startIndex, endIndex, dimension, alignment);
660                 if (subOverlap != null)
661                     singleOverlap = subOverlap;
662             }
663         }
664
665         return singleOverlap;
666     }
667
668     // [couldn't parallelizeWithParentSequence be used instead? or LayoutFeeder.separateSequence?]
669
private void separateGroupContent(LayoutInterval separate, int outPos, int dimension, int alignment) {
670         LayoutInterval group = separate.getParent();
671         assert group.isParallel();
672         LayoutInterval remainder = null;
673         LayoutInterval remainderGroup = null;
674         LayoutRegion remainderSpace = null;
675
676         for (int i=0; i < group.getSubIntervalCount(); ) {
677             LayoutInterval li = group.getSubInterval(i);
678             if (li != separate) {
679                 assert li.getAlignment() == (alignment^1);
680                 layoutModel.removeInterval(li);
681                 if (remainder == null) {
682                     remainder = li;
683                 }
684                 else {
685                     if (remainderGroup == null) {
686                         remainderGroup = new LayoutInterval(PARALLEL);
687                         remainderGroup.setAlignment(alignment^1);
688                         remainderGroup.setGroupAlignment(alignment^1);
689                         layoutModel.addInterval(remainder, remainderGroup, 0);
690                         remainder = remainderGroup;
691                     }
692                     layoutModel.addInterval(li, remainderGroup, -1);
693                 }
694                 if (!li.isEmptySpace()) {
695                     if (remainderSpace == null) {
696                         remainderSpace = new LayoutRegion();
697                     }
698                     remainderSpace.expand(li.getCurrentSpace());
699                 }
700             }
701             else i++;
702         }
703         remainder.setCurrentSpace(remainderSpace);
704
705         LayoutInterval remainderGap;
706         int remainderPos = remainderSpace.positions[dimension][alignment];
707         if (LayoutRegion.isValidCoordinate(outPos)) {
708             int gapSize = alignment == LEADING ? remainderPos - outPos : outPos - remainderPos;
709             remainderGap = new LayoutInterval(SINGLE);
710             remainderGap.setSizes(NOT_EXPLICITLY_DEFINED, gapSize, Short.MAX_VALUE);
711         }
712         else { // take the existing gap next to group [this case is not used currently]
713
remainderGap = LayoutInterval.getDirectNeighbor(group, alignment, false);
714             if (remainderGap != null && remainderGap.isEmptySpace()) {
715                 layoutModel.removeInterval(remainderGap);
716                 // [should check for last interval in parent]
717
LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(group, alignment, true);
718                 outPos = neighbor != null ?
719                     neighbor.getCurrentSpace().positions[dimension][alignment^1] :
720                     group.getParent().getCurrentSpace().positions[dimension][alignment];
721                 int gapSize = alignment == LEADING ? remainderPos - outPos : outPos - remainderPos;
722                 resizeInterval(remainderGap, gapSize);
723             }
724             else remainderGap = null;
725         }
726         if (remainderGap != null) {
727             LayoutInterval seq;
728             if (remainder.isSequential()) {
729                 seq = remainder;
730             }
731             else {
732                  seq = new LayoutInterval(SEQUENTIAL);
733                  layoutModel.setIntervalAlignment(remainder, DEFAULT);
734                  layoutModel.addInterval(remainder, seq, 0);
735             }
736             layoutModel.addInterval(remainderGap, seq, alignment == LEADING ? 0 : -1);
737             layoutModel.addInterval(seq, group, -1);
738             group.getCurrentSpace().positions[dimension][alignment] = outPos;
739         }
740         else {
741             layoutModel.addInterval(remainder, group, -1);
742         }
743     }
744
745     /**
746      * Makes given interval parallel with part of its parent sequence.
747      */

748     void parallelizeWithParentSequence(LayoutInterval interval, int endIndex, int dimension) {
749         LayoutInterval parent = interval.getParent();
750         assert parent.isParallel();
751         LayoutInterval parParent = parent;
752         while (!parParent.getParent().isSequential()) {
753             parParent = parParent.getParent();
754         }
755         LayoutInterval parentSeq = parParent.getParent();
756
757         int startIndex = parentSeq.indexOf(parParent);
758         if (endIndex < 0)
759             endIndex = parentSeq.getSubIntervalCount() - 1;
760         else if (startIndex > endIndex) {
761             int temp = startIndex;
762             startIndex = endIndex;
763             endIndex = temp;
764         }
765
766         layoutModel.removeInterval(interval);
767         if (interval.getAlignment() == DEFAULT) {
768             layoutModel.setIntervalAlignment(interval, parent.getGroupAlignment());
769         }
770         addParallelWithSequence(interval, parentSeq, startIndex, endIndex, dimension);
771
772         if (parent.getSubIntervalCount() == 1) {
773             addContent(layoutModel.removeInterval(parent, 0),
774                        parent.getParent(),
775                        layoutModel.removeInterval(parent));
776         }
777         else if (parent.getSubIntervalCount() == 0) {
778             layoutModel.removeInterval(parent);
779         }
780     }
781
782     void addParallelWithSequence(LayoutInterval interval, LayoutInterval seq, int startIndex, int endIndex, int dimension) {
783         LayoutInterval group;
784         if (startIndex > 0 || endIndex < seq.getSubIntervalCount()-1) {
785             group = new LayoutInterval(PARALLEL);
786             if (interval.getAlignment() != DEFAULT) {
787                 group.setGroupAlignment(interval.getAlignment());
788             }
789             int startPos = LayoutUtils.getVisualPosition(seq.getSubInterval(startIndex), dimension, LEADING);
790             int endPos = LayoutUtils.getVisualPosition(seq.getSubInterval(endIndex), dimension, TRAILING);
791             group.getCurrentSpace().set(dimension, startPos, endPos);
792
793             if (startIndex != endIndex) {
794                 LayoutInterval subSeq = new LayoutInterval(SEQUENTIAL);
795                 subSeq.setAlignment(seq.getAlignment());
796                 for (int n=endIndex-startIndex+1; n > 0; n--) {
797                     layoutModel.addInterval(layoutModel.removeInterval(seq, startIndex), subSeq, -1);
798                 }
799                 layoutModel.addInterval(subSeq, group, 0);
800             }
801             else {
802                 layoutModel.addInterval(layoutModel.removeInterval(seq, startIndex), group, 0);
803             }
804             layoutModel.addInterval(group, seq, startIndex);
805         }
806         else {
807             group = seq.getParent();
808         }
809         layoutModel.addInterval(interval, group, -1);
810     }
811
812     int optimizeGaps(LayoutInterval group, int dimension) {
813         boolean anyAlignedLeading = false; // if false the group is open at leading edge
814
boolean anyAlignedTrailing = false; // if false the group is open at trailing edge
815
boolean anyAlignedBoth = false;
816         boolean anyGapLeading = false; // if true there is some gap at the leading edge
817
boolean anyGapTrailing = false; // if true there is some gap at the trailing edge
818
boolean sameMinGapLeading = true; // if true all intervals are aligned with the same gap at leading edge
819
boolean sameMinGapTrailing = true; // if true all intervals are aligned with the same gap at trailing edge
820
int commonGapLeadingSize = Integer.MIN_VALUE;
821         int commonGapTrailingSize = Integer.MIN_VALUE;
822
823         // first analyze the group
824
for (int i=0; i < group.getSubIntervalCount(); i++) {
825             LayoutInterval li = group.getSubInterval(i);
826             if (li.isEmptySpace()) { // remove container supporting gap
827
if (group.getSubIntervalCount() > 1) {
828                     layoutModel.removeInterval(group, i);
829                     i--;
830                     continue;
831                 }
832             }
833
834             boolean leadingAlign = false;
835             boolean trailingAlign = false;
836             LayoutInterval leadingGap = null;
837             LayoutInterval trailingGap = null;
838             boolean contentResizing = false;
839             boolean noResizing = false;
840             if (li.isSequential()) {
841                 // find out effective alignment of the sequence content without border gaps
842
boolean leadGapRes = false;
843                 boolean trailGapRes = false;
844                 for (int j=0; j < li.getSubIntervalCount(); j++) {
845                     LayoutInterval sub = li.getSubInterval(j);
846                     if (j == 0 && sub.isEmptySpace()) {
847                         leadingGap = sub;
848                         leadGapRes = LayoutInterval.wantResize(sub);
849                     }
850                     else if (j+1 == li.getSubIntervalCount() && sub.isEmptySpace()) {
851                         trailingGap = sub;
852                         trailGapRes = LayoutInterval.wantResize(sub);
853                     }
854                     else if (!contentResizing && LayoutInterval.wantResize(sub)) {
855                         contentResizing = true;
856                     }
857                 }
858                 if (!contentResizing) {
859                     if (leadGapRes || trailGapRes) {
860                         leadingAlign = trailGapRes && !leadGapRes;
861                         trailingAlign = leadGapRes && !trailGapRes;
862                     }
863                     else noResizing = true;
864                 }
865             }
866             else if (LayoutInterval.wantResize(li))
867                 contentResizing = true;
868             else
869                 noResizing = true;
870
871             if (contentResizing)
872                 leadingAlign = trailingAlign = true;
873             else if (noResizing) {
874                 int alignment = li.getAlignment();
875                 leadingAlign = alignment == LEADING;
876                 trailingAlign = alignment == TRAILING;
877             }
878
879             if (leadingAlign) {
880                 anyAlignedLeading = true;
881                 if (trailingAlign)
882                     anyAlignedBoth = true;
883             }
884             if (trailingAlign) {
885                 anyAlignedTrailing = true;
886             }
887
888             if (leadingGap != null) {
889                 anyGapLeading = true;
890                 if (sameMinGapLeading) {
891                     int size = leadingAlign || leadingGap.getMinimumSize() == USE_PREFERRED_SIZE ?
892                                leadingGap.getPreferredSize() : leadingGap.getMinimumSize();
893                     if (commonGapLeadingSize != Integer.MIN_VALUE) {
894                         if (size != commonGapLeadingSize)
895                             sameMinGapLeading = false;
896                     }
897                     else commonGapLeadingSize = size;
898                 }
899             }
900             else sameMinGapLeading = false;
901
902             if (trailingGap != null) {
903                 anyGapTrailing = true;
904                 if (sameMinGapTrailing) {
905                     int size = trailingAlign || trailingGap.getMinimumSize() == USE_PREFERRED_SIZE ?
906                                trailingGap.getPreferredSize() : trailingGap.getMinimumSize();
907                     if (commonGapTrailingSize != Integer.MIN_VALUE) {
908                         if (size != commonGapTrailingSize)
909                             sameMinGapTrailing = false;
910                     }
911                     else commonGapTrailingSize = size;
912                 }
913             }
914             else sameMinGapTrailing = false;
915         }
916
917         if (group.getSubIntervalCount() <= 1 || (!anyGapLeading && !anyGapTrailing)) {
918             return -1;
919         }
920
921         if (!anyAlignedBoth) {
922             // can't reduce common minimum gap if anything aligned to opposite egde
923
if (anyAlignedTrailing)
924                 sameMinGapLeading = false;
925             if (anyAlignedLeading)
926                 sameMinGapTrailing = false;
927         }
928
929         int[] groupOuterPos = group.getCurrentSpace().positions[dimension];
930         assert groupOuterPos[LEADING] > Short.MIN_VALUE && groupOuterPos[TRAILING] > Short.MIN_VALUE;
931         int groupInnerPosLeading = LayoutUtils.getOutermostComponent(group, dimension, LEADING)
932                                        .getCurrentSpace().positions[dimension][LEADING];
933         int groupInnerPosTrailing = LayoutUtils.getOutermostComponent(group, dimension, TRAILING)
934                                         .getCurrentSpace().positions[dimension][TRAILING];
935
936         boolean defaultPaddingLeading = false; // if true, the leading padding has default preferred size
937
boolean defaultPaddingTrailing = false; // if true, the trailing padding has default preferred size
938
boolean resizingGapLeading = false;
939         boolean resizingGapTrailing = false;
940
941         // remove gaps where needed
942
for (int i=0; i < group.getSubIntervalCount(); i++) {
943             LayoutInterval li = group.getSubInterval(i);
944             if (li.isSequential()) {
945                 if (anyGapLeading && (!anyAlignedLeading || sameMinGapLeading)) {
946                     LayoutInterval gap = li.getSubInterval(0);
947                     if (gap.isEmptySpace()) {
948                         if (gap.getPreferredSize() == NOT_EXPLICITLY_DEFINED
949                             && isEndingDefaultGapEffective(li, dimension, LEADING))
950                         { // default padding to be used as common gap
951
defaultPaddingLeading = true;
952                         }
953                         if (gap.getMaximumSize() >= Short.MAX_VALUE) {
954                             if (li.getAlignment() == LEADING) // need to change alignment as we removed resizing gap
955
layoutModel.setIntervalAlignment(li, TRAILING);
956                             if (!anyAlignedLeading) // resizability goes out of the group
957
resizingGapLeading = true;
958                         }
959                         layoutModel.removeInterval(gap);
960                     }
961                 }
962
963                 if (anyGapTrailing && (!anyAlignedTrailing || sameMinGapTrailing)) {
964                     LayoutInterval gap = li.getSubInterval(li.getSubIntervalCount() - 1);
965                     if (gap.isEmptySpace()) {
966                         if (gap.getPreferredSize() == NOT_EXPLICITLY_DEFINED
967                             && isEndingDefaultGapEffective(li, dimension, TRAILING))
968                         { // default padding to be used as common gap
969
defaultPaddingTrailing = true;
970                         }
971                         if (gap.getMaximumSize() >= Short.MAX_VALUE) {
972                             if (li.getAlignment() == TRAILING) // need to change alignment as we removed resizing gap
973
layoutModel.setIntervalAlignment(li, LEADING);
974                             if (!anyAlignedTrailing) // resizability goes out of the group
975
resizingGapTrailing = true;
976                         }
977                         layoutModel.removeInterval(gap);
978                     }
979                 }
980
981                 if (li.getSubIntervalCount() == 1) {
982                     // only one interval remained in sequence - cancel the sequence
983
layoutModel.removeInterval(group, i); // removes li from group
984
LayoutInterval sub = layoutModel.removeInterval(li, 0); // removes last interval from li
985
layoutModel.setIntervalAlignment(sub, li.getRawAlignment());
986                     layoutModel.addInterval(sub, group, i);
987                 }
988             }
989         }
990
991         LayoutInterval leadingGap = null;
992         LayoutInterval trailingGap = null;
993
994         if (anyGapLeading) {
995             if (!anyAlignedLeading) { // group is open at leading edge
996
int size = groupInnerPosLeading - groupOuterPos[LEADING];
997                 if (size > 0 || defaultPaddingLeading) {
998                     leadingGap = new LayoutInterval(SINGLE);
999                     if (!defaultPaddingLeading) {
1000                        leadingGap.setPreferredSize(size);
1001                        if (!resizingGapLeading)
1002                            leadingGap.setMinimumSize(USE_PREFERRED_SIZE);
1003                    }
1004                    if (resizingGapLeading) {
1005                        leadingGap.setMaximumSize(Short.MAX_VALUE);
1006                    }
1007                }
1008            }
1009            else if (sameMinGapLeading) {
1010                leadingGap = new LayoutInterval(SINGLE);
1011// int size = commonGapLeading.getMinimumSize();
1012
// if (size == USE_PREFERRED_SIZE)
1013
// size = commonGapLeading.getPreferredSize();
1014
// leadingGap.setSizes(size, size, USE_PREFERRED_SIZE);
1015
leadingGap.setSizes(commonGapLeadingSize, commonGapLeadingSize, USE_PREFERRED_SIZE);
1016            }
1017        }
1018        if (anyGapTrailing) {
1019            if (!anyAlignedTrailing) { // group is open at trailing edge
1020
int size = groupOuterPos[TRAILING] - groupInnerPosTrailing;
1021                if (size > 0 || defaultPaddingTrailing) {
1022                    trailingGap = new LayoutInterval(SINGLE);
1023                    if (!defaultPaddingTrailing) {
1024                        trailingGap.setPreferredSize(size);
1025                        if (!resizingGapTrailing)
1026                            trailingGap.setMinimumSize(USE_PREFERRED_SIZE);
1027                    }
1028                    if (resizingGapTrailing) {
1029                        trailingGap.setMaximumSize(Short.MAX_VALUE);
1030                    }
1031                }
1032            }
1033            else if (sameMinGapTrailing) {
1034                trailingGap = new LayoutInterval(SINGLE);
1035// int size = commonGapTrailing.getMinimumSize();
1036
// if (size == USE_PREFERRED_SIZE)
1037
// size = commonGapTrailing.getPreferredSize();
1038
// trailingGap.setSizes(size, size, USE_PREFERRED_SIZE);
1039
trailingGap.setSizes(commonGapTrailingSize, commonGapTrailingSize, USE_PREFERRED_SIZE);
1040            }
1041        }
1042
1043        if (leadingGap != null || trailingGap != null) {
1044            if (leadingGap != null || !LayoutRegion.isValidCoordinate(groupOuterPos[LEADING])) {
1045                groupOuterPos[LEADING] = groupInnerPosLeading;
1046            }
1047            if (trailingGap != null || !LayoutRegion.isValidCoordinate(groupOuterPos[TRAILING])) {
1048                groupOuterPos[TRAILING] = groupInnerPosTrailing;
1049            }
1050            groupOuterPos[CENTER] = (groupInnerPosLeading + groupInnerPosTrailing) / 2;
1051            if (leadingGap != null) {
1052                group = insertGap(leadingGap, group, groupInnerPosLeading, dimension, LEADING);
1053            }
1054            if (trailingGap != null) {
1055                group = insertGap(trailingGap, group, groupInnerPosTrailing, dimension, TRAILING);
1056            }
1057            LayoutInterval parent = group.getParent();
1058            return parent != null ? parent.indexOf(group) : -1;//idx;
1059
}
1060        return -1;
1061    }
1062
1063    private boolean isEndingDefaultGapEffective(LayoutInterval seq, int dimension, int alignment) {
1064        assert seq.isSequential() && (alignment == LEADING || alignment == TRAILING);
1065        int idx = alignment == LEADING ? 0 : seq.getSubIntervalCount() - 1;
1066        int d = alignment == LEADING ? 1 : -1;
1067        LayoutInterval gap = seq.getSubInterval(idx);
1068        LayoutInterval neighbor = seq.getSubInterval(idx+d);
1069
1070        if (LayoutInterval.getEffectiveAlignment(neighbor, alignment) == alignment) {
1071            return true; // aligned
1072
}
1073        else {
1074            int prefDistance = LayoutUtils.getSizeOfDefaultGap(gap, visualMapper);
1075            int pos1 = neighbor.getCurrentSpace().positions[dimension][alignment];
1076            LayoutInterval outerNeighbor = LayoutInterval.getNeighbor(gap, alignment, true, true, false);
1077            int pos2 = outerNeighbor != null ?
1078                       outerNeighbor.getCurrentSpace().positions[dimension][alignment^1] :
1079                       LayoutInterval.getRoot(seq).getCurrentSpace().positions[dimension][alignment];
1080            int currentDistance = (pos1 - pos2) * d;
1081            return currentDistance <= prefDistance;
1082        }
1083    }
1084
1085    boolean cutStartingGap(LayoutInterval group, int size, int dimension, int alignment) {
1086        assert group.isGroup() && size > 0 && (alignment == LEADING || alignment == TRAILING);
1087        // [just very simple impl. for now - considering just one sequence...]
1088
LayoutInterval seq = null;
1089        if (group.isSequential()) {
1090            seq = group;
1091        }
1092        else if (group.getSubIntervalCount() == 1) {
1093            LayoutInterval li = group.getSubInterval(0);
1094            if (li.isSequential() && LayoutInterval.isAlignedAtBorder(li, alignment))
1095                seq = li;
1096        }
1097        if (seq != null && seq.getSubIntervalCount() > 1) {
1098            LayoutInterval gap = seq.getSubInterval(alignment == LEADING ? 0 : seq.getSubIntervalCount()-1);
1099            LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(gap, alignment^1, true);
1100            if (gap != null && gap.isEmptySpace() && neighbor != null) {
1101                int currentSize = gap.getPreferredSize();
1102                if (currentSize == NOT_EXPLICITLY_DEFINED) {
1103                    currentSize = LayoutRegion.distance(group.getCurrentSpace(), neighbor.getCurrentSpace(),
1104                                                        dimension, alignment, alignment)
1105                                  * (alignment == TRAILING ? -1 : 1);
1106                }
1107                if (currentSize >= size) {
1108                    if (currentSize > size)
1109                        resizeInterval(gap, currentSize - size);
1110                    else
1111                        layoutModel.removeInterval(gap);
1112                    return true;
1113                }
1114            }
1115        }
1116        return false;
1117    }
1118
1119    /**
1120     * Inserts a gap before or after specified interval. If in a sequence, the
1121     * method takes care about merging gaps if there is already some as neighbor.
1122     * Expects the actual positions of the sequence are up-to-date.
1123     * @param gap the gap to be inserted
1124     * @param interval the interval before or after which the gap is added
1125     * @param pos expected real position of the end of the interval where the gap
1126     * is added (need not correspond to that stored in the interval)
1127     * @param dimension
1128     * @param alignment at which side of the interval the gap is added (LEADING or TRAILING)
1129     */

1130    LayoutInterval insertGap(LayoutInterval gap, LayoutInterval interval, int pos, int dimension, int alignment) {
1131        assert alignment == LEADING || alignment == TRAILING;
1132        assert !interval.isSequential();
1133        assert gap.isEmptySpace();
1134
1135        LayoutInterval parent = interval.getParent();
1136        if (parent == null) {
1137            assert interval.isParallel();
1138            parent = interval;
1139            if (parent.getSubIntervalCount() > 1) {
1140                LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
1141                seq.getCurrentSpace().set(dimension,
1142                    (alignment == LEADING) ? pos : interval.getCurrentSpace().positions[dimension][LEADING],
1143                    (alignment == LEADING) ? interval.getCurrentSpace().positions[dimension][TRAILING] : pos);
1144                layoutModel.addInterval(seq, parent, -1);
1145                interval = new LayoutInterval(PARALLEL);
1146                interval.getCurrentSpace().set(dimension, parent.getCurrentSpace());
1147                layoutModel.addInterval(interval, seq, 0);
1148                while (parent.getSubIntervalCount() > 1) {
1149                    layoutModel.addInterval(layoutModel.removeInterval(parent, 0), interval, -1);
1150                }
1151                parent = seq;
1152            }
1153            else {
1154                interval = parent.getSubInterval(0);
1155                if (interval.isSequential()) {
1156                    parent = interval;
1157                    int subIdx = alignment == LEADING ? 0 : parent.getSubIntervalCount()-1;
1158                    interval = parent.getSubInterval(subIdx);
1159                    if (interval.isEmptySpace()) {
1160                        subIdx += alignment == LEADING ? 1 : -1;
1161                        LayoutInterval neighbor = subIdx >= 0 && subIdx < parent.getSubIntervalCount() ?
1162                                                  parent.getSubInterval(subIdx) : null;
1163                        int[] outerSpace = parent.getParent().getCurrentSpace().positions[dimension];
1164                        int otherPos = neighbor != null ? neighbor.getCurrentSpace().positions[dimension][alignment] :
1165                                                          outerSpace[alignment^1];
1166                        int mergedSize = (outerSpace[alignment] - otherPos) * (alignment == LEADING ? -1 : 1);
1167                        eatGap(interval, gap, mergedSize);
1168                        return neighbor != null ? neighbor : interval;
1169                    }
1170                }
1171                else {
1172                    LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
1173                    seq.getCurrentSpace().set(dimension,
1174                        (alignment == LEADING) ? pos : interval.getCurrentSpace().positions[dimension][LEADING],
1175                        (alignment == LEADING) ? interval.getCurrentSpace().positions[dimension][TRAILING] : pos);
1176                    layoutModel.addInterval(seq, parent, -1);
1177                    layoutModel.removeInterval(interval);
1178                    layoutModel.addInterval(interval, seq, -1);
1179                    parent = seq;
1180                }
1181            }
1182        }
1183        if (parent.isSequential()) {
1184            // we can't use insertGapIntoSequence here because 'pos' can be special
1185
LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(interval, alignment, false);
1186            if (neighbor != null && neighbor.isEmptySpace()) {
1187                LayoutInterval next = LayoutInterval.getDirectNeighbor(neighbor, alignment, false);
1188                int otherPos = next != null ? next.getCurrentSpace().positions[dimension][alignment^1] :
1189                                              parent.getCurrentSpace().positions[dimension][alignment];
1190                int mergedSize = (pos - otherPos) * (alignment == LEADING ? 1 : -1);
1191                eatGap(neighbor, gap, mergedSize);
1192            }
1193            else {
1194                int idx = parent.indexOf(interval) + (alignment == LEADING ? 0 : 1);
1195                layoutModel.addInterval(gap, parent, idx);
1196            }
1197        }
1198        else { // parallel parent
1199
LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
1200            int idx = layoutModel.removeInterval(interval);
1201            seq.setAlignment(interval.getAlignment());
1202            seq.getCurrentSpace().set(dimension,
1203                (alignment == LEADING) ? pos : interval.getCurrentSpace().positions[dimension][LEADING],
1204                (alignment == LEADING) ? interval.getCurrentSpace().positions[dimension][TRAILING] : pos);
1205            layoutModel.addInterval(seq, parent, idx);
1206            layoutModel.setIntervalAlignment(interval, DEFAULT);
1207            layoutModel.addInterval(interval, seq, 0);
1208            layoutModel.addInterval(gap, seq, alignment == LEADING ? 0 : 1);
1209        }
1210
1211        return interval;
1212    }
1213
1214    int insertGapIntoSequence(LayoutInterval gap, LayoutInterval seq, int index, int dimension) {
1215        assert gap.isEmptySpace();
1216        LayoutInterval otherGap = null;
1217        int alignment = DEFAULT;
1218        if (index >= 0 && index < seq.getSubIntervalCount()) {
1219            otherGap = seq.getSubInterval(index);
1220            if (otherGap.isEmptySpace())
1221                alignment = TRAILING;
1222        }
1223        if (alignment == DEFAULT && index > 0) {
1224            otherGap = seq.getSubInterval(index-1);
1225            if (otherGap.isEmptySpace())
1226                alignment = LEADING;
1227        }
1228        if (alignment == DEFAULT) {
1229            layoutModel.addInterval(gap, seq, index);
1230                return index; // gap was added normally
1231
}
1232
1233        int pos1, pos2;
1234        LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(otherGap, alignment, true);
1235        pos1 = neighbor != null ?
1236               neighbor.getCurrentSpace().positions[dimension][alignment^1] :
1237               seq.getCurrentSpace().positions[dimension][alignment];
1238        neighbor = LayoutInterval.getDirectNeighbor(otherGap, alignment^1, true);
1239        pos2 = neighbor != null ?
1240               neighbor.getCurrentSpace().positions[dimension][alignment] :
1241               seq.getCurrentSpace().positions[dimension][alignment^1];
1242
1243        eatGap(otherGap, gap, Math.abs(pos2 - pos1));
1244        return alignment == LEADING ? index-1 : index; // gap was eaten
1245
}
1246
1247    void eatGap(LayoutInterval main, LayoutInterval eaten, int currentMergedSize) {
1248        int min;
1249        int min1 = main.getMinimumSize();
1250        if (min1 == USE_PREFERRED_SIZE)
1251            min1 = main.getPreferredSize();
1252        int min2 = eaten.getMinimumSize();
1253        if (min2 == USE_PREFERRED_SIZE)
1254            min2 = eaten.getPreferredSize();
1255
1256        if (min1 == 0)
1257            min = min2;
1258        else if (min2 == 0)
1259            min = min1;
1260        else if (!LayoutInterval.canResize(main) && !LayoutInterval.canResize(eaten))
1261            min = USE_PREFERRED_SIZE;
1262        else if (min1 == NOT_EXPLICITLY_DEFINED || min2 == NOT_EXPLICITLY_DEFINED)
1263            min = NOT_EXPLICITLY_DEFINED;
1264        else
1265            min = min1 + min2;
1266
1267        int pref;
1268        int pref1 = main.getPreferredSize();
1269        int pref2 = eaten.getPreferredSize();
1270
1271        if (pref1 == 0)
1272            pref = pref2;
1273        else if (pref2 == 0)
1274            pref = pref1;
1275        else if (pref1 == NOT_EXPLICITLY_DEFINED || pref2 == NOT_EXPLICITLY_DEFINED)
1276            pref = currentMergedSize;
1277        else
1278            pref = pref1 + pref2;
1279
1280        int max = main.getMaximumSize() >= Short.MAX_VALUE || eaten.getMaximumSize() >= Short.MAX_VALUE ?
1281                  Short.MAX_VALUE : USE_PREFERRED_SIZE;
1282
1283        layoutModel.setIntervalSize(main, min, pref, max);
1284        if (eaten.getParent() != null) {
1285            layoutModel.removeInterval(eaten);
1286        }
1287    }
1288    
1289    void mergeAdjacentGaps(Set updatedContainers) {
1290        Iterator it = layoutModel.getAllComponents();
1291        while (it.hasNext()) {
1292            LayoutComponent comp = (LayoutComponent) it.next();
1293            if (!comp.isLayoutContainer())
1294                continue;
1295            
1296            boolean updated = false;
1297            for (int dim=0; dim<DIM_COUNT; dim++) {
1298                LayoutInterval interval = comp.getLayoutRoot(dim);
1299                updated = updated || mergeAdjacentGaps(interval, dim);
1300            }
1301            if (updated) {
1302                updatedContainers.add(comp);
1303            }
1304        }
1305    }
1306    
1307    boolean mergeAdjacentGaps(LayoutInterval root, int dimension) {
1308        assert root.isGroup();
1309        boolean updated = false;
1310        if (root.isSequential()) {
1311            for (int i=0; i<root.getSubIntervalCount(); i++) {
1312                LayoutInterval interval = root.getSubInterval(i);
1313                if (interval.isEmptySpace() && ((i+1) < root.getSubIntervalCount())) {
1314                    LayoutInterval next = root.getSubInterval(i+1);
1315                    if (next.isEmptySpace()) {
1316                        if ((i+2) < root.getSubIntervalCount()) {
1317                            LayoutInterval nextNext = root.getSubInterval(i+2);
1318                            if (nextNext.isEmptySpace()) {
1319                                i--; // The merged gap should be merged with nextNext gap
1320
}
1321                        }
1322                        updated = true;
1323                        eatGap(interval, next, NOT_EXPLICITLY_DEFINED);
1324                    }
1325                }
1326            }
1327        }
1328        Iterator iter = root.getSubIntervals();
1329        while (iter.hasNext()) {
1330            LayoutInterval subInterval = (LayoutInterval)iter.next();
1331            if (subInterval.isGroup()) {
1332                updated = updated || mergeAdjacentGaps(subInterval, dimension);
1333            }
1334        }
1335        return updated;
1336    }
1337
1338    void suppressResizingOfSurroundingGaps(LayoutInterval interval) {
1339        LayoutInterval parent = interval.getParent();
1340        while (parent != null) {
1341            if (parent.isSequential()) {
1342                for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) {
1343                    LayoutInterval sub = (LayoutInterval) it.next();
1344                    if (sub != interval && sub.isEmptySpace() && LayoutInterval.canResize(sub)) {
1345                        int pref = sub.getPreferredSize();
1346                        int min = sub.getMinimumSize() != pref ? USE_PREFERRED_SIZE : pref;
1347                        int max = USE_PREFERRED_SIZE;
1348                        layoutModel.setIntervalSize(sub, min, pref, max);
1349                    }
1350                }
1351            }
1352            else if (!LayoutInterval.canResize(parent))
1353                break;
1354            interval = parent;
1355            parent = interval.getParent();
1356        }
1357    }
1358}
1359
Popular Tags