KickJava   Java API By Example, From Geeks To Geeks.

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


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 /**
26  * This class collects various static methods for examining the layout.
27  * For modifying methods see LayoutOperations class.
28  *
29  * @author Tomas Pavek
30  */

31
32 public class LayoutUtils implements LayoutConstants {
33
34     private LayoutUtils() {
35     }
36
37     public static LayoutInterval getAdjacentEmptySpace(LayoutComponent comp, int dimension, int direction) {
38         LayoutInterval interval = comp.getLayoutInterval(dimension);
39         LayoutInterval parent;
40         while ((parent = interval.getParent()) != null) {
41             if (parent.isSequential()) {
42                 int index = parent.indexOf(interval);
43                 if (direction == LEADING) {
44                     if (index == 0) {
45                         interval = parent;
46                     } else {
47                         LayoutInterval candidate = parent.getSubInterval(index-1);
48                         return candidate.isEmptySpace() ? candidate : null;
49                     }
50                 } else {
51                     if (index == parent.getSubIntervalCount()-1) {
52                         interval = parent;
53                     } else {
54                         LayoutInterval candidate = parent.getSubInterval(index+1);
55                         return candidate.isEmptySpace() ? candidate : null;
56                     }
57                 }
58             } else {
59                 // PENDING how should we determine the space: isAlignedAtBorder, isPlacedAtBorder, any?
60
if (LayoutInterval.isPlacedAtBorder(interval, dimension, direction)) {
61                     interval = parent;
62                 } else {
63                     return null;
64                 }
65             }
66         }
67         return null;
68     }
69
70     // -----
71
// package private utils
72

73     static LayoutInterval getOutermostComponent(LayoutInterval interval, int dimension, int alignment) {
74         if (interval.isComponent()) {
75             return interval;
76         }
77
78         assert alignment == LEADING || alignment == TRAILING;
79
80         if (interval.isSequential()) {
81             int d = alignment == LEADING ? 1 : -1;
82             int i = alignment == LEADING ? 0 : interval.getSubIntervalCount()-1;
83             while (i >= 0 && i < interval.getSubIntervalCount()) {
84                 LayoutInterval li = interval.getSubInterval(i);
85                 if (li.isEmptySpace()) {
86                     i += d;
87                 }
88 // else if (li.isComponent()) {
89
// return li;
90
// }
91
else {
92                     return getOutermostComponent(li, dimension, alignment);
93                 }
94             }
95 // return null;
96
}
97         else if (interval.isParallel()) {
98             LayoutInterval best = null;
99             int pos = Integer.MAX_VALUE;
100             for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) {
101                 LayoutInterval li = getOutermostComponent(interval.getSubInterval(i), dimension, alignment);
102                 if (li != null) {
103                     if (LayoutInterval.isAlignedAtBorder(li, interval, alignment)) {
104                         return li;
105                     }
106                     int p = li.getCurrentSpace().positions[dimension][alignment]
107                             * (alignment == LEADING ? 1 : -1);
108                     if (p < pos) {
109                         best = li;
110                         pos = p;
111                     }
112                 }
113             }
114             return best;
115         }
116 // else if (interval.isComponent()) {
117
// return interval;
118
// }
119
return null;
120     }
121     
122     /**
123      * Returns size of the empty space represented by the given layout interval.
124      *
125      * @param interval layout interval that represents padding.
126      * @return size of the padding.
127      */

128     static int getSizeOfDefaultGap(LayoutInterval interval, VisualMapper visualMapper) {
129         assert interval.isEmptySpace();
130         LayoutInterval parent = interval.getParent();
131         if (parent.isParallel())
132             return interval.getPreferredSize();
133         
134         // Find intervals that contain sources and targets
135
LayoutInterval candidate = interval;
136         LayoutInterval srcInt = null;
137         LayoutInterval targetInt = null;
138         while ((parent != null) && ((srcInt == null) || (targetInt == null))) {
139             int index = parent.indexOf(candidate);
140             if ((srcInt == null) && (index > 0)) {
141                 srcInt = parent.getSubInterval(index-1);
142             }
143             if ((targetInt == null) && (index < parent.getSubIntervalCount()-1)) {
144                 targetInt = parent.getSubInterval(index+1);
145             }
146             if ((srcInt == null) || (targetInt == null)) {
147                 do {
148                     candidate = parent;
149                     parent = parent.getParent();
150                 } while ((parent != null) && parent.isParallel());
151             }
152         }
153         
154         // Find sources and targets inside srcInt and targetInt
155
List sources = edgeSubComponents(srcInt, TRAILING);
156         List targets = edgeSubComponents(targetInt, LEADING);
157
158         // Calculate size of gap from sources and targets and their positions
159
return getSizeOfDefaultGap(sources, targets, visualMapper, null, Collections.EMPTY_MAP);
160     }
161
162     static int getSizeOfDefaultGap(List sources, List targets, VisualMapper visualMapper,
163                                    String JavaDoc contId, Map boundsMap) {
164         if (((sources != null) && (sources.isEmpty()))
165             || ((targets != null) && (targets.isEmpty()))) {
166             return 0; // Preferred gap not between components
167
}
168         sources = (sources == null) ? Collections.EMPTY_LIST : sources;
169         targets = (targets == null) ? Collections.EMPTY_LIST : targets;
170         int size = 0;
171         boolean containerGap = false;
172         int containerGapAlignment = -1;
173         LayoutInterval temp = null;
174         if (sources.isEmpty()) {
175             if (targets.isEmpty()) {
176                 return 0;
177             } else {
178                 // Leading container gap
179
containerGap = true;
180                 containerGapAlignment = LEADING;
181                 temp = (LayoutInterval)targets.get(0);
182             }
183         } else {
184             temp = (LayoutInterval)sources.get(0);
185             if (targets.isEmpty()) {
186                 // Trailing container gap
187
containerGap = true;
188                 containerGapAlignment = TRAILING;
189             }
190         }
191         int dimension = (temp == temp.getComponent().getLayoutInterval(HORIZONTAL)) ? HORIZONTAL : VERTICAL;
192         // Calculate max of sources and min of targets
193
int max = Short.MIN_VALUE;
194         int min = Short.MAX_VALUE;
195         boolean positionsNotUpdated = false;
196         Iterator iter = sources.iterator();
197         while (iter.hasNext()) {
198             LayoutInterval source = (LayoutInterval)iter.next();
199             LayoutRegion region = sizeOfEmptySpaceHelper(source, boundsMap);
200             int trailing = region.positions[dimension][TRAILING];
201             if (trailing == LayoutRegion.UNKNOWN) {
202                 positionsNotUpdated = true; break;
203             } else {
204                 max = Math.max(max, trailing);
205             }
206         }
207         iter = targets.iterator();
208         while (iter.hasNext()) {
209             LayoutInterval target = (LayoutInterval)iter.next();
210             LayoutRegion region = sizeOfEmptySpaceHelper(target, boundsMap);
211             int leading = region.positions[dimension][LEADING];
212             if (leading == LayoutRegion.UNKNOWN) {
213                 positionsNotUpdated = true; break;
214             } else {
215                 min = Math.min(min, leading);
216             }
217         }
218         if (containerGap) {
219             iter = sources.isEmpty() ? targets.iterator() : sources.iterator();
220             while (iter.hasNext()) {
221                 LayoutInterval interval = (LayoutInterval)iter.next();
222                 LayoutComponent component = interval.getComponent();
223                 LayoutRegion region = sizeOfEmptySpaceHelper(interval, boundsMap);
224                 String JavaDoc parentId = (contId == null) ? component.getParent().getId() : contId;
225                 int padding = visualMapper.getPreferredPaddingInParent(parentId, component.getId(), dimension, containerGapAlignment);
226                 int position = region.positions[dimension][containerGapAlignment];
227                 int delta = (containerGapAlignment == LEADING) ? (position - min) : (max - position);
228                 if (!positionsNotUpdated) padding -= delta;
229                 size = Math.max(size, padding);
230             }
231         } else {
232             Iterator srcIter = sources.iterator();
233             while (srcIter.hasNext()) {
234                 LayoutInterval srcCandidate = (LayoutInterval)srcIter.next();
235                 String JavaDoc srcId = srcCandidate.getComponent().getId();
236                 LayoutRegion srcRegion = sizeOfEmptySpaceHelper(srcCandidate, boundsMap);
237                 int srcDelta = max - srcRegion.positions[dimension][TRAILING];
238                 Iterator targetIter = targets.iterator();
239                 while (targetIter.hasNext()) {
240                     LayoutInterval targetCandidate = (LayoutInterval)targetIter.next();
241                     String JavaDoc targetId = targetCandidate.getComponent().getId();
242                     LayoutRegion targetRegion = sizeOfEmptySpaceHelper(targetCandidate, boundsMap);
243                     int targetDelta = targetRegion.positions[dimension][LEADING] - min;
244                     int padding = visualMapper.getPreferredPadding(srcId,
245                         targetId, dimension, LEADING, VisualMapper.PADDING_RELATED);
246                     if (!positionsNotUpdated) padding -= srcDelta + targetDelta;
247                     size = Math.max(size, padding);
248                 }
249             }
250         }
251         return size;
252     }
253
254     private static LayoutRegion sizeOfEmptySpaceHelper(LayoutInterval interval, Map boundsMap) {
255         LayoutComponent component = interval.getComponent();
256         String JavaDoc compId = component.getId();
257         if (boundsMap.containsKey(compId)) {
258             return (LayoutRegion)boundsMap.get(compId);
259         } else {
260             return interval.getCurrentSpace();
261         }
262     }
263
264     static int getVisualPosition(LayoutInterval interval, int dimension, int alignment) {
265         if (interval.isEmptySpace()) {
266             assert alignment == LEADING || alignment == TRAILING;
267             LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(interval, alignment, false);
268             if (neighbor != null) {
269                 interval = neighbor;
270                 alignment ^= 1;
271             }
272             else interval = LayoutInterval.getFirstParent(interval, PARALLEL);
273         }
274         return interval.getCurrentSpace().positions[dimension][alignment];
275     }
276
277     /**
278      * Returns list of components that reside in the <code>root</code>
279      * layout interval - the list contains only components whose layout
280      * intervals lie at the specified edge (<code>LEADING</code>
281      * or <code>TRAILING</code>) of the <code>root</code> layout interval.
282      *
283      * @param root layout interval that will be scanned.
284      * @param edge the requested edge the components shoul be next to.
285      * @return <code>List</code> of <code>LayoutInterval</code>s that
286      * represent <code>LayoutComponent</code>s.
287      */

288     static List edgeSubComponents(LayoutInterval root, int edge) {
289         List components = null;
290         List candidates = new LinkedList();
291         if (root != null) {
292             components = new LinkedList();
293             candidates.add(root);
294         }
295         while (!candidates.isEmpty()) {
296             LayoutInterval candidate = (LayoutInterval)candidates.get(0);
297             candidates.remove(candidate);
298             if (candidate.isGroup()) {
299                 if (candidate.isSequential()) {
300                     int index = (edge == LEADING) ? 0 : candidate.getSubIntervalCount()-1;
301                     candidates.add(candidate.getSubInterval(index));
302                 } else {
303                     Iterator subs = candidate.getSubIntervals();
304                     while (subs.hasNext()) {
305                         candidates.add(subs.next());
306                     }
307                 }
308             } else if (candidate.isComponent()) {
309                 components.add(candidate);
310             }
311         }
312         return components;
313     }
314
315     /**
316      * Computes whether a space overlaps with content of given interval.
317      * The difference from LayoutRegion.overlap(...) is that this method goes
318      * recursivelly down to components in case interval is a group - does not
319      * use the union space for whole group (which might be inaccurate).
320      */

321     static boolean contentOverlap(LayoutRegion space, LayoutInterval interval, int dimension) {
322         return contentOverlap(space, interval, -1, -1, dimension);
323     }
324
325     static boolean contentOverlap(LayoutRegion space, LayoutInterval interval, int fromIndex, int toIndex, int dimension) {
326         LayoutRegion examinedSpace = interval.getCurrentSpace();
327         if (!interval.isGroup()) {
328             return LayoutRegion.overlap(space, examinedSpace, dimension, 0);
329         }
330         boolean overlap = !examinedSpace.isSet(dimension)
331                           || LayoutRegion.overlap(space, examinedSpace, dimension, 0);
332         if (overlap) {
333             if (fromIndex < 0)
334                 fromIndex = 0;
335             if (toIndex < 0)
336                 toIndex = interval.getSubIntervalCount()-1;
337             assert fromIndex <= toIndex;
338
339             overlap = false;
340             for (int i=fromIndex; i <= toIndex; i++) {
341                 LayoutInterval li = interval.getSubInterval(i);
342                 if (!li.isEmptySpace() && contentOverlap(space, li, dimension)) {
343                     overlap = true;
344                     break;
345                 }
346             }
347         }
348         return overlap;
349     }
350
351     /**
352      * Finds out whether components under one interval overlap with components
353      * under another interval (in given dimension).
354      */

355     static boolean contentOverlap(LayoutInterval interval1, LayoutInterval interval2, int dimension) {
356         return contentOverlap(interval1, interval2, -1, -1, dimension);
357     }
358
359     /**
360      * @param fromIndex initial index of sub-interval in interval2
361      * @param toIndex last index to consider under interval2
362      */

363     static boolean contentOverlap(LayoutInterval interval1, LayoutInterval interval2,
364                                   int fromIndex, int toIndex, int dimension)
365     {
366         if (!interval2.isGroup()) {
367             if (!interval1.isGroup()) {
368                 return LayoutRegion.overlap(interval1.getCurrentSpace(),
369                                             interval2.getCurrentSpace(), dimension, 0);
370             }
371             LayoutInterval temp = interval1;
372             interval1 = interval2;
373             interval2 = temp;
374         }
375
376         // [more efficient algorithm based on region merging and ordering could be found...]
377
List int2list = null;
378         List addList = null;
379         Iterator it1 = getComponentIterator(interval1);
380         while (it1.hasNext()) {
381             LayoutRegion space1 = ((LayoutInterval)it1.next()).getCurrentSpace();
382             Iterator it2 = int2list != null ?
383                            int2list.iterator() :
384                            getComponentIterator(interval2, fromIndex, toIndex);
385             if (int2list == null && it1.hasNext()) {
386                 int2list = new LinkedList();
387                 addList = int2list;
388             }
389             while (it2.hasNext()) {
390                 LayoutInterval li2 = (LayoutInterval) it2.next();
391                 if (LayoutRegion.overlap(space1, li2.getCurrentSpace(), dimension, 0))
392                     return true;
393                 if (addList != null)
394                     addList.add(li2);
395             }
396             addList = null;
397         }
398         return false;
399     }
400
401     /**
402      * Checks the layout structure of the orthogonal dimension whether
403      * an overlap of a component interval with another interval (or its
404      * subintervals) is prevented - i.e. if in the orthogonal dimension the
405      * intervals of the same components are placed sequentially.
406      */

407     static boolean isOverlapPreventedInOtherDimension(LayoutInterval compInterval,
408                                                       LayoutInterval interval,
409                                                       int dimension)
410     {
411         int otherDim = dimension^1;
412         compInterval = (LayoutInterval) getComponentIterator(compInterval).next();
413         LayoutComponent component = compInterval.getComponent();
414         LayoutInterval otherCompInterval = component.getLayoutInterval(otherDim);
415         Iterator it = getComponentIterator(interval);
416         assert it.hasNext();
417         do {
418             LayoutComponent comp = ((LayoutInterval)it.next()).getComponent();
419             LayoutInterval otherInterval = comp.getLayoutInterval(otherDim);
420             LayoutInterval parent = LayoutInterval.getCommonParent(otherCompInterval, otherInterval);
421             if (parent == null || parent.isParallel())
422                 return false;
423         }
424         while (it.hasNext());
425         return true;
426     }
427
428     static Iterator getComponentIterator(LayoutInterval interval) {
429         return new ComponentIterator(interval, 0, interval.getSubIntervalCount()-1);
430     }
431
432     static Iterator getComponentIterator(LayoutInterval interval, int startIndex, int endIndex) {
433         return new ComponentIterator(interval, startIndex, endIndex);
434     }
435
436     private static class ComponentIterator implements Iterator {
437         private LayoutInterval root;
438         private int startIndex, endIndex;
439         private boolean initialized;
440         private int index;
441         private LayoutInterval next;
442
443         ComponentIterator(LayoutInterval interval, int startIndex, int endIndex) {
444             root = interval;
445             this.startIndex = startIndex;
446             this.endIndex = endIndex;
447             findNext();
448             initialized = true;
449         }
450
451         private void findNext() {
452             LayoutInterval parent;
453             int idx;
454             if (next == null) {
455                 if (initialized)
456                     return;
457                 if (!root.isGroup()) {
458                     if (root.isComponent()) {
459                         next = root;
460                     }
461                     return;
462                 }
463                 parent = root; // let's start from root
464
idx = startIndex;
465             }
466             else if (next != root) { // somewhere in the structure
467
parent = next.getParent();
468                 idx = index + 1;
469             }
470             else { // root is component, already used
471
next = null;
472                 return;
473             }
474
475             next = null;
476             do {
477                 while (idx < parent.getSubIntervalCount()) {
478                     if (parent == root && idx > endIndex)
479                         return; // out of the root set
480
LayoutInterval sub = parent.getSubInterval(idx);
481                     if (sub.isComponent()) { // that's it
482
next = sub;
483                         index = idx;
484                         return;
485                     }
486                     if (sub.isGroup()) { // go down
487
parent = sub;
488                         idx = 0;
489                     }
490                     else idx++;
491                 }
492                 if (parent != root) { // go up
493
idx = parent.getParent().indexOf(parent) + 1;
494                     parent = parent.getParent();
495                 }
496                 else break; // all scanned
497
}
498             while (true);
499         }
500
501         public boolean hasNext() {
502             return next != null;
503         }
504
505         public Object JavaDoc next() {
506             if (next == null)
507                 throw new NoSuchElementException();
508
509             Object JavaDoc ret = next;
510             findNext();
511             return ret;
512         }
513
514         public void remove() {
515             throw new UnsupportedOperationException JavaDoc();
516         }
517     }
518 }
519
Popular Tags