KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > BoxView


1 /*
2  * @(#)BoxView.java 1.63 06/07/28
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text;
8
9 import java.io.PrintStream JavaDoc;
10 import java.util.Vector JavaDoc;
11 import java.awt.*;
12 import javax.swing.event.DocumentEvent JavaDoc;
13 import javax.swing.SizeRequirements JavaDoc;
14
15 /**
16  * A view that arranges its children into a box shape by tiling
17  * its children along an axis. The box is somewhat like that
18  * found in TeX where there is alignment of the
19  * children, flexibility of the children is considered, etc.
20  * This is a building block that might be useful to represent
21  * things like a collection of lines, paragraphs,
22  * lists, columns, pages, etc. The axis along which the children are tiled is
23  * considered the major axis. The orthoginal axis is the minor axis.
24  * <p>
25  * Layout for each axis is handled separately by the methods
26  * <code>layoutMajorAxis</code> and <code>layoutMinorAxis</code>.
27  * Subclasses can change the layout algorithm by
28  * reimplementing these methods. These methods will be called
29  * as necessary depending upon whether or not there is cached
30  * layout information and the cache is considered
31  * valid. These methods are typically called if the given size
32  * along the axis changes, or if <code>layoutChanged</code> is
33  * called to force an updated layout. The <code>layoutChanged</code>
34  * method invalidates cached layout information, if there is any.
35  * The requirements published to the parent view are calculated by
36  * the methods <code>calculateMajorAxisRequirements</code>
37  * and <code>calculateMinorAxisRequirements</code>.
38  * If the layout algorithm is changed, these methods will
39  * likely need to be reimplemented.
40  *
41  * @author Timothy Prinzing
42  * @version 1.63 07/28/06
43  */

44 public class BoxView extends CompositeView JavaDoc {
45
46     /**
47      * Constructs a <code>BoxView</code>.
48      *
49      * @param elem the element this view is responsible for
50      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
51      */

52     public BoxView(Element JavaDoc elem, int axis) {
53     super(elem);
54     tempRect = new Rectangle();
55     this.majorAxis = axis;
56
57     majorOffsets = new int[0];
58     majorSpans = new int[0];
59     majorReqValid = false;
60     majorAllocValid = false;
61     minorOffsets = new int[0];
62     minorSpans = new int[0];
63     minorReqValid = false;
64     minorAllocValid = false;
65     }
66
67     /**
68      * Fetches the tile axis property. This is the axis along which
69      * the child views are tiled.
70      *
71      * @return the major axis of the box, either
72      * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
73      *
74      * @since 1.3
75      */

76     public int getAxis() {
77     return majorAxis;
78     }
79
80     /**
81      * Sets the tile axis property. This is the axis along which
82      * the child views are tiled.
83      *
84      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
85      *
86      * @since 1.3
87      */

88     public void setAxis(int axis) {
89     boolean axisChanged = (axis != majorAxis);
90     majorAxis = axis;
91     if (axisChanged) {
92         preferenceChanged(null, true, true);
93     }
94     }
95
96     /**
97      * Invalidates the layout along an axis. This happens
98      * automatically if the preferences have changed for
99      * any of the child views. In some cases the layout
100      * may need to be recalculated when the preferences
101      * have not changed. The layout can be marked as
102      * invalid by calling this method. The layout will
103      * be updated the next time the <code>setSize</code> method
104      * is called on this view (typically in paint).
105      *
106      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
107      *
108      * @since 1.3
109      */

110     public void layoutChanged(int axis) {
111     if (axis == majorAxis) {
112         majorAllocValid = false;
113     } else {
114         minorAllocValid = false;
115     }
116     }
117
118     /**
119      * Determines if the layout is valid along the given axis.
120      *
121      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
122      *
123      * @since 1.4
124      */

125     protected boolean isLayoutValid(int axis) {
126     if (axis == majorAxis) {
127         return majorAllocValid;
128     } else {
129         return minorAllocValid;
130     }
131     }
132
133     /**
134      * Paints a child. By default
135      * that is all it does, but a subclass can use this to paint
136      * things relative to the child.
137      *
138      * @param g the graphics context
139      * @param alloc the allocated region to paint into
140      * @param index the child index, >= 0 && < getViewCount()
141      */

142     protected void paintChild(Graphics g, Rectangle alloc, int index) {
143     View JavaDoc child = getView(index);
144     child.paint(g, alloc);
145     }
146
147     // --- View methods ---------------------------------------------
148

149     /**
150      * Invalidates the layout and resizes the cache of
151      * requests/allocations. The child allocations can still
152      * be accessed for the old layout, but the new children
153      * will have an offset and span of 0.
154      *
155      * @param index the starting index into the child views to insert
156      * the new views; this should be a value >= 0 and <= getViewCount
157      * @param length the number of existing child views to remove;
158      * This should be a value >= 0 and <= (getViewCount() - offset)
159      * @param elems the child views to add; this value can be
160      * <code>null</code>to indicate no children are being added
161      * (useful to remove)
162      */

163     public void replace(int index, int length, View JavaDoc[] elems) {
164     super.replace(index, length, elems);
165
166     // invalidate cache
167
int nInserted = (elems != null) ? elems.length : 0;
168     majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);
169     majorSpans = updateLayoutArray(majorSpans, index, nInserted);
170     majorReqValid = false;
171     majorAllocValid = false;
172     minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);
173     minorSpans = updateLayoutArray(minorSpans, index, nInserted);
174     minorReqValid = false;
175     minorAllocValid = false;
176     }
177
178     /**
179      * Resizes the given layout array to match the new number of
180      * child views. The current number of child views are used to
181      * produce the new array. The contents of the old array are
182      * inserted into the new array at the appropriate places so that
183      * the old layout information is transferred to the new array.
184      *
185      * @param oldArray the original layout array
186      * @param offset location where new views will be inserted
187      * @param nInserted the number of child views being inserted;
188      * therefore the number of blank spaces to leave in the
189      * new array at location <code>offset</code>
190      * @return the new layout array
191      */

192     int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
193     int n = getViewCount();
194     int[] newArray = new int[n];
195
196     System.arraycopy(oldArray, 0, newArray, 0, offset);
197     System.arraycopy(oldArray, offset,
198              newArray, offset + nInserted, n - nInserted - offset);
199     return newArray;
200     }
201
202     /**
203      * Forwards the given <code>DocumentEvent</code> to the child views
204      * that need to be notified of the change to the model.
205      * If a child changed its requirements and the allocation
206      * was valid prior to forwarding the portion of the box
207      * from the starting child to the end of the box will
208      * be repainted.
209      *
210      * @param ec changes to the element this view is responsible
211      * for (may be <code>null</code> if there were no changes)
212      * @param e the change information from the associated document
213      * @param a the current allocation of the view
214      * @param f the factory to use to rebuild if the view has children
215      * @see #insertUpdate
216      * @see #removeUpdate
217      * @see #changedUpdate
218      */

219     protected void forwardUpdate(DocumentEvent.ElementChange JavaDoc ec,
220                  DocumentEvent JavaDoc e, Shape a, ViewFactory JavaDoc f) {
221     boolean wasValid = isLayoutValid(majorAxis);
222     super.forwardUpdate(ec, e, a, f);
223
224     // determine if a repaint is needed
225
if (wasValid && (! isLayoutValid(majorAxis))) {
226         // Repaint is needed because one of the tiled children
227
// have changed their span along the major axis. If there
228
// is a hosting component and an allocated shape we repaint.
229
Component c = getContainer();
230         if ((a != null) && (c != null)) {
231         int pos = e.getOffset();
232         int index = getViewIndexAtPosition(pos);
233         Rectangle alloc = getInsideAllocation(a);
234         if (majorAxis == X_AXIS) {
235             alloc.x += majorOffsets[index];
236             alloc.width -= majorOffsets[index];
237         } else {
238             alloc.y += minorOffsets[index];
239             alloc.height -= minorOffsets[index];
240         }
241         c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
242         }
243     }
244     }
245
246     /**
247      * This is called by a child to indicate its
248      * preferred span has changed. This is implemented to
249      * throw away cached layout information so that new
250      * calculations will be done the next time the children
251      * need an allocation.
252      *
253      * @param child the child view
254      * @param width true if the width preference should change
255      * @param height true if the height preference should change
256      */

257     public void preferenceChanged(View JavaDoc child, boolean width, boolean height) {
258     boolean majorChanged = (majorAxis == X_AXIS) ? width : height;
259     boolean minorChanged = (majorAxis == X_AXIS) ? height : width;
260     if (majorChanged) {
261         majorReqValid = false;
262         majorAllocValid = false;
263     }
264     if (minorChanged) {
265         minorReqValid = false;
266         minorAllocValid = false;
267     }
268     super.preferenceChanged(child, width, height);
269     }
270
271     /**
272      * Gets the resize weight. A value of 0 or less is not resizable.
273      *
274      * @param axis may be either <code>View.X_AXIS</code> or
275      * <code>View.Y_AXIS</code>
276      * @return the weight
277      * @exception IllegalArgumentException for an invalid axis
278      */

279     public int getResizeWeight(int axis) {
280     checkRequests(axis);
281         if (axis == majorAxis) {
282         if ((majorRequest.preferred != majorRequest.minimum) ||
283         (majorRequest.preferred != majorRequest.maximum)) {
284         return 1;
285         }
286     } else {
287         if ((minorRequest.preferred != minorRequest.minimum) ||
288         (minorRequest.preferred != minorRequest.maximum)) {
289         return 1;
290         }
291     }
292     return 0;
293     }
294
295     /**
296      * Sets the size of the view along an axis. This should cause
297      * layout of the view along the given axis.
298      *
299      * @param axis may be either <code>View.X_AXIS</code> or
300      * <code>View.Y_AXIS</code>
301      * @param span the span to layout to >= 0
302      */

303     void setSpanOnAxis(int axis, float span) {
304     if (axis == majorAxis) {
305         if (majorSpan != (int) span) {
306         majorAllocValid = false;
307         }
308         if (! majorAllocValid) {
309         // layout the major axis
310
majorSpan = (int) span;
311         checkRequests(majorAxis);
312         layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);
313         majorAllocValid = true;
314
315         // flush changes to the children
316
updateChildSizes();
317         }
318     } else {
319         if (((int) span) != minorSpan) {
320         minorAllocValid = false;
321         }
322         if (! minorAllocValid) {
323         // layout the minor axis
324
minorSpan = (int) span;
325         checkRequests(axis);
326         layoutMinorAxis(minorSpan, axis, minorOffsets, minorSpans);
327         minorAllocValid = true;
328
329         // flush changes to the children
330
updateChildSizes();
331         }
332     }
333     }
334
335     /**
336      * Propagates the current allocations to the child views.
337      */

338     void updateChildSizes() {
339     int n = getViewCount();
340     if (majorAxis == X_AXIS) {
341         for (int i = 0; i < n; i++) {
342         View JavaDoc v = getView(i);
343         v.setSize((float) majorSpans[i], (float) minorSpans[i]);
344         }
345     } else {
346         for (int i = 0; i < n; i++) {
347         View JavaDoc v = getView(i);
348         v.setSize((float) minorSpans[i], (float) majorSpans[i]);
349         }
350     }
351     }
352
353     /**
354      * Returns the size of the view along an axis. This is implemented
355      * to return zero.
356      *
357      * @param axis may be either <code>View.X_AXIS</code> or
358      * <code>View.Y_AXIS</code>
359      * @return the current span of the view along the given axis, >= 0
360      */

361     float getSpanOnAxis(int axis) {
362     if (axis == majorAxis) {
363         return majorSpan;
364     } else {
365         return minorSpan;
366     }
367     }
368
369     /**
370      * Sets the size of the view. This should cause
371      * layout of the view if the view caches any layout
372      * information. This is implemented to call the
373      * layout method with the sizes inside of the insets.
374      *
375      * @param width the width >= 0
376      * @param height the height >= 0
377      */

378     public void setSize(float width, float height) {
379     layout((int)(width - getLeftInset() - getRightInset()),
380            (int)(height - getTopInset() - getBottomInset()));
381     }
382
383     /**
384      * Renders the <code>BoxView</code> using the given
385      * rendering surface and area
386      * on that surface. Only the children that intersect
387      * the clip bounds of the given <code>Graphics</code>
388      * will be rendered.
389      *
390      * @param g the rendering surface to use
391      * @param allocation the allocated region to render into
392      * @see View#paint
393      */

394     public void paint(Graphics g, Shape allocation) {
395     Rectangle alloc = (allocation instanceof Rectangle) ?
396                        (Rectangle)allocation : allocation.getBounds();
397     int n = getViewCount();
398     int x = alloc.x + getLeftInset();
399     int y = alloc.y + getTopInset();
400     Rectangle clip = g.getClipBounds();
401     for (int i = 0; i < n; i++) {
402         tempRect.x = x + getOffset(X_AXIS, i);
403         tempRect.y = y + getOffset(Y_AXIS, i);
404         tempRect.width = getSpan(X_AXIS, i);
405         tempRect.height = getSpan(Y_AXIS, i);
406             if (tempRect.intersects(clip)) {
407         paintChild(g, tempRect, i);
408         }
409     }
410     }
411
412     /**
413      * Fetches the allocation for the given child view.
414      * This enables finding out where various views
415      * are located. This is implemented to return
416      * <code>null</code> if the layout is invalid,
417      * otherwise the superclass behavior is executed.
418      *
419      * @param index the index of the child, >= 0 && < getViewCount()
420      * @param a the allocation to this view
421      * @return the allocation to the child; or <code>null</code>
422      * if <code>a</code> is <code>null</code>;
423      * or <code>null</code> if the layout is invalid
424      */

425     public Shape getChildAllocation(int index, Shape a) {
426     if (a != null) {
427         Shape ca = super.getChildAllocation(index, a);
428         if ((ca != null) && (! isAllocationValid())) {
429         // The child allocation may not have been set yet.
430
Rectangle r = (ca instanceof Rectangle) ?
431             (Rectangle) ca : ca.getBounds();
432         if ((r.width == 0) && (r.height == 0)) {
433             return null;
434         }
435         }
436         return ca;
437     }
438     return null;
439     }
440
441     /**
442      * Provides a mapping from the document model coordinate space
443      * to the coordinate space of the view mapped to it. This makes
444      * sure the allocation is valid before calling the superclass.
445      *
446      * @param pos the position to convert >= 0
447      * @param a the allocated region to render into
448      * @return the bounding box of the given position
449      * @exception BadLocationException if the given position does
450      * not represent a valid location in the associated document
451      * @see View#modelToView
452      */

453     public Shape modelToView(int pos, Shape a, Position.Bias JavaDoc b) throws BadLocationException JavaDoc {
454     if (! isAllocationValid()) {
455         Rectangle alloc = a.getBounds();
456         setSize(alloc.width, alloc.height);
457     }
458     return super.modelToView(pos, a, b);
459     }
460
461     /**
462      * Provides a mapping from the view coordinate space to the logical
463      * coordinate space of the model.
464      *
465      * @param x x coordinate of the view location to convert >= 0
466      * @param y y coordinate of the view location to convert >= 0
467      * @param a the allocated region to render into
468      * @return the location within the model that best represents the
469      * given point in the view >= 0
470      * @see View#viewToModel
471      */

472     public int viewToModel(float x, float y, Shape a, Position.Bias JavaDoc[] bias) {
473     if (! isAllocationValid()) {
474         Rectangle alloc = a.getBounds();
475         setSize(alloc.width, alloc.height);
476     }
477     return super.viewToModel(x, y, a, bias);
478     }
479
480     /**
481      * Determines the desired alignment for this view along an
482      * axis. This is implemented to give the total alignment
483      * needed to position the children with the alignment points
484      * lined up along the axis orthoginal to the axis that is
485      * being tiled. The axis being tiled will request to be
486      * centered (i.e. 0.5f).
487      *
488      * @param axis may be either <code>View.X_AXIS</code>
489      * or <code>View.Y_AXIS</code>
490      * @return the desired alignment >= 0.0f && <= 1.0f; this should
491      * be a value between 0.0 and 1.0 where 0 indicates alignment at the
492      * origin and 1.0 indicates alignment to the full span
493      * away from the origin; an alignment of 0.5 would be the
494      * center of the view
495      * @exception IllegalArgumentException for an invalid axis
496      */

497     public float getAlignment(int axis) {
498     checkRequests(axis);
499     if (axis == majorAxis) {
500         return majorRequest.alignment;
501     } else {
502         return minorRequest.alignment;
503     }
504     }
505
506     /**
507      * Determines the preferred span for this view along an
508      * axis.
509      *
510      * @param axis may be either <code>View.X_AXIS</code>
511      * or <code>View.Y_AXIS</code>
512      * @return the span the view would like to be rendered into >= 0;
513      * typically the view is told to render into the span
514      * that is returned, although there is no guarantee;
515      * the parent may choose to resize or break the view
516      * @exception IllegalArgumentException for an invalid axis type
517      */

518     public float getPreferredSpan(int axis) {
519     checkRequests(axis);
520     float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
521         getTopInset() + getBottomInset();
522     if (axis == majorAxis) {
523         return ((float)majorRequest.preferred) + marginSpan;
524     } else {
525         return ((float)minorRequest.preferred) + marginSpan;
526     }
527     }
528
529     /**
530      * Determines the minimum span for this view along an
531      * axis.
532      *
533      * @param axis may be either <code>View.X_AXIS</code>
534      * or <code>View.Y_AXIS</code>
535      * @return the span the view would like to be rendered into >= 0;
536      * typically the view is told to render into the span
537      * that is returned, although there is no guarantee;
538      * the parent may choose to resize or break the view
539      * @exception IllegalArgumentException for an invalid axis type
540      */

541     public float getMinimumSpan(int axis) {
542     checkRequests(axis);
543     float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
544         getTopInset() + getBottomInset();
545     if (axis == majorAxis) {
546         return ((float)majorRequest.minimum) + marginSpan;
547     } else {
548         return ((float)minorRequest.minimum) + marginSpan;
549     }
550     }
551
552     /**
553      * Determines the maximum span for this view along an
554      * axis.
555      *
556      * @param axis may be either <code>View.X_AXIS</code>
557      * or <code>View.Y_AXIS</code>
558      * @return the span the view would like to be rendered into >= 0;
559      * typically the view is told to render into the span
560      * that is returned, although there is no guarantee;
561      * the parent may choose to resize or break the view
562      * @exception IllegalArgumentException for an invalid axis type
563      */

564     public float getMaximumSpan(int axis) {
565     checkRequests(axis);
566     float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
567         getTopInset() + getBottomInset();
568     if (axis == majorAxis) {
569         return ((float)majorRequest.maximum) + marginSpan;
570     } else {
571         return ((float)minorRequest.maximum) + marginSpan;
572     }
573     }
574
575     // --- local methods ----------------------------------------------------
576

577     /**
578      * Are the allocations for the children still
579      * valid?
580      *
581      * @return true if allocations still valid
582      */

583     protected boolean isAllocationValid() {
584     return (majorAllocValid && minorAllocValid);
585     }
586    
587     /**
588      * Determines if a point falls before an allocated region.
589      *
590      * @param x the X coordinate >= 0
591      * @param y the Y coordinate >= 0
592      * @param innerAlloc the allocated region; this is the area
593      * inside of the insets
594      * @return true if the point lies before the region else false
595      */

596     protected boolean isBefore(int x, int y, Rectangle innerAlloc) {
597     if (majorAxis == View.X_AXIS) {
598         return (x < innerAlloc.x);
599     } else {
600         return (y < innerAlloc.y);
601     }
602     }
603
604     /**
605      * Determines if a point falls after an allocated region.
606      *
607      * @param x the X coordinate >= 0
608      * @param y the Y coordinate >= 0
609      * @param innerAlloc the allocated region; this is the area
610      * inside of the insets
611      * @return true if the point lies after the region else false
612      */

613     protected boolean isAfter(int x, int y, Rectangle innerAlloc) {
614     if (majorAxis == View.X_AXIS) {
615         return (x > (innerAlloc.width + innerAlloc.x));
616     } else {
617         return (y > (innerAlloc.height + innerAlloc.y));
618     }
619     }
620
621     /**
622      * Fetches the child view at the given coordinates.
623      *
624      * @param x the X coordinate >= 0
625      * @param y the Y coordinate >= 0
626      * @param alloc the parents inner allocation on entry, which should
627      * be changed to the childs allocation on exit
628      * @return the view
629      */

630     protected View JavaDoc getViewAtPoint(int x, int y, Rectangle alloc) {
631     int n = getViewCount();
632     if (majorAxis == View.X_AXIS) {
633         if (x < (alloc.x + majorOffsets[0])) {
634         childAllocation(0, alloc);
635         return getView(0);
636         }
637         for (int i = 0; i < n; i++) {
638         if (x < (alloc.x + majorOffsets[i])) {
639             childAllocation(i - 1, alloc);
640             return getView(i - 1);
641         }
642         }
643         childAllocation(n - 1, alloc);
644         return getView(n - 1);
645     } else {
646         if (y < (alloc.y + majorOffsets[0])) {
647         childAllocation(0, alloc);
648         return getView(0);
649         }
650         for (int i = 0; i < n; i++) {
651         if (y < (alloc.y + majorOffsets[i])) {
652             childAllocation(i - 1, alloc);
653             return getView(i - 1);
654         }
655         }
656         childAllocation(n - 1, alloc);
657         return getView(n - 1);
658     }
659     }
660
661     /**
662      * Allocates a region for a child view.
663      *
664      * @param index the index of the child view to
665      * allocate, >= 0 && < getViewCount()
666      * @param alloc the allocated region
667      */

668     protected void childAllocation(int index, Rectangle alloc) {
669     alloc.x += getOffset(X_AXIS, index);
670     alloc.y += getOffset(Y_AXIS, index);
671     alloc.width = getSpan(X_AXIS, index);
672     alloc.height = getSpan(Y_AXIS, index);
673     }
674
675     /**
676      * Perform layout on the box
677      *
678      * @param width the width (inside of the insets) >= 0
679      * @param height the height (inside of the insets) >= 0
680      */

681     protected void layout(int width, int height) {
682     setSpanOnAxis(X_AXIS, width);
683     setSpanOnAxis(Y_AXIS, height);
684     }
685
686     /**
687      * Returns the current width of the box. This is the width that
688      * it was last allocated.
689      * @return the current width of the box
690      */

691     public int getWidth() {
692     int span;
693     if (majorAxis == X_AXIS) {
694         span = majorSpan;
695     } else {
696         span = minorSpan;
697     }
698     span += getLeftInset() - getRightInset();
699     return span;
700     }
701
702     /**
703      * Returns the current height of the box. This is the height that
704      * it was last allocated.
705      * @return the current height of the box
706      */

707     public int getHeight() {
708     int span;
709     if (majorAxis == Y_AXIS) {
710         span = majorSpan;
711     } else {
712         span = minorSpan;
713     }
714     span += getTopInset() - getBottomInset();
715     return span;
716     }
717
718     /**
719      * Performs layout for the major axis of the box (i.e. the
720      * axis that it represents). The results of the layout should
721      * be placed in the given arrays which represent the allocations
722      * to the children along the major axis.
723      *
724      * @param targetSpan the total span given to the view, which
725      * would be used to layout the children
726      * @param axis the axis being layed out
727      * @param offsets the offsets from the origin of the view for
728      * each of the child views; this is a return value and is
729      * filled in by the implementation of this method
730      * @param spans the span of each child view; this is a return
731      * value and is filled in by the implementation of this method
732      * @return the offset and span for each child view in the
733      * offsets and spans parameters
734      */

735     protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
736     /*
737      * first pass, calculate the preferred sizes
738      * and the flexibility to adjust the sizes.
739      */

740     long preferred = 0;
741     int n = getViewCount();
742     for (int i = 0; i < n; i++) {
743         View JavaDoc v = getView(i);
744         spans[i] = (int) v.getPreferredSpan(axis);
745         preferred += spans[i];
746     }
747
748     /*
749      * Second pass, expand or contract by as much as possible to reach
750      * the target span.
751      */

752
753     // determine the adjustment to be made
754
long desiredAdjustment = targetSpan - preferred;
755     float adjustmentFactor = 0.0f;
756         int[] diffs = null;
757
758     if (desiredAdjustment != 0) {
759             long totalSpan = 0;
760             diffs = new int[n];
761             for (int i = 0; i < n; i++) {
762                 View JavaDoc v = getView(i);
763                 int tmp;
764                 if (desiredAdjustment < 0) {
765                     tmp = (int)v.getMinimumSpan(axis);
766                     diffs[i] = spans[i] - tmp;
767                 } else {
768                     tmp = (int)v.getMaximumSpan(axis);
769                     diffs[i] = tmp - spans[i];
770                 }
771                 totalSpan += tmp;
772             }
773             
774             float maximumAdjustment = Math.abs(totalSpan - preferred);
775                 adjustmentFactor = desiredAdjustment / maximumAdjustment;
776                 adjustmentFactor = Math.min(adjustmentFactor, 1.0f);
777                 adjustmentFactor = Math.max(adjustmentFactor, -1.0f);
778             }
779
780     // make the adjustments
781
int totalOffset = 0;
782     for (int i = 0; i < n; i++) {
783         offsets[i] = totalOffset;
784             if (desiredAdjustment != 0) {
785                 float adjF = adjustmentFactor * diffs[i];
786                 spans[i] += Math.round(adjF);
787             }
788         totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
789     }
790     }
791
792     /**
793      * Performs layout for the minor axis of the box (i.e. the
794      * axis orthoginal to the axis that it represents). The results
795      * of the layout should be placed in the given arrays which represent
796      * the allocations to the children along the minor axis.
797      *
798      * @param targetSpan the total span given to the view, which
799      * would be used to layout the children
800      * @param axis the axis being layed out
801      * @param offsets the offsets from the origin of the view for
802      * each of the child views; this is a return value and is
803      * filled in by the implementation of this method
804      * @param spans the span of each child view; this is a return
805      * value and is filled in by the implementation of this method
806      * @return the offset and span for each child view in the
807      * offsets and spans parameters
808      */

809     protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
810     int n = getViewCount();
811     for (int i = 0; i < n; i++) {
812         View JavaDoc v = getView(i);
813         int max = (int) v.getMaximumSpan(axis);
814         if (max < targetSpan) {
815         // can't make the child this wide, align it
816
float align = v.getAlignment(axis);
817         offsets[i] = (int) ((targetSpan - max) * align);
818         spans[i] = max;
819         } else {
820         // make it the target width, or as small as it can get.
821
int min = (int)v.getMinimumSpan(axis);
822         offsets[i] = 0;
823         spans[i] = Math.max(min, targetSpan);
824         }
825     }
826     }
827
828     /**
829      * Calculates the size requirements for the major axis
830      * </code>axis</code>.
831      *
832      * @param axis the axis being studied
833      * @param r the <code>SizeRequirements</code> object;
834      * if <code>null</code> one will be created
835      * @return the newly initialized <code>SizeRequirements</code> object
836      * @see javax.swing.SizeRequirements
837      */

838     protected SizeRequirements JavaDoc calculateMajorAxisRequirements(int axis, SizeRequirements JavaDoc r) {
839     // calculate tiled request
840
float min = 0;
841     float pref = 0;
842     float max = 0;
843
844     int n = getViewCount();
845     for (int i = 0; i < n; i++) {
846         View JavaDoc v = getView(i);
847         min += v.getMinimumSpan(axis);
848         pref += v.getPreferredSpan(axis);
849         max += v.getMaximumSpan(axis);
850     }
851
852     if (r == null) {
853         r = new SizeRequirements JavaDoc();
854     }
855     r.alignment = 0.5f;
856     r.minimum = (int) min;
857     r.preferred = (int) pref;
858     r.maximum = (int) max;
859     return r;
860     }
861
862     /**
863      * Calculates the size requirements for the minor axis
864      * <code>axis</code>.
865      *
866      * @param axis the axis being studied
867      * @param r the <code>SizeRequirements</code> object;
868      * if <code>null</code> one will be created
869      * @return the newly initialized <code>SizeRequirements</code> object
870      * @see javax.swing.SizeRequirements
871      */

872     protected SizeRequirements JavaDoc calculateMinorAxisRequirements(int axis, SizeRequirements JavaDoc r) {
873     int min = 0;
874     long pref = 0;
875     int max = Integer.MAX_VALUE;
876     int n = getViewCount();
877     for (int i = 0; i < n; i++) {
878         View JavaDoc v = getView(i);
879         min = Math.max((int) v.getMinimumSpan(axis), min);
880         pref = Math.max((int) v.getPreferredSpan(axis), pref);
881         max = Math.max((int) v.getMaximumSpan(axis), max);
882     }
883
884     if (r == null) {
885         r = new SizeRequirements JavaDoc();
886         r.alignment = 0.5f;
887     }
888     r.preferred = (int) pref;
889     r.minimum = min;
890     r.maximum = max;
891     return r;
892     }
893
894     /**
895      * Checks the request cache and update if needed.
896      * @param axis the axis being studied
897      * @exception IllegalArgumentException if <code>axis</code> is
898      * neither <code>View.X_AXIS</code> nor </code>View.Y_AXIS</code>
899      */

900     void checkRequests(int axis) {
901     if ((axis != X_AXIS) && (axis != Y_AXIS)) {
902         throw new IllegalArgumentException JavaDoc("Invalid axis: " + axis);
903     }
904     if (axis == majorAxis) {
905             if (!majorReqValid) {
906                 majorRequest = calculateMajorAxisRequirements(axis,
907                                                               majorRequest);
908                 majorReqValid = true;
909             }
910     } else if (! minorReqValid) {
911         minorRequest = calculateMinorAxisRequirements(axis, minorRequest);
912         minorReqValid = true;
913     }
914     }
915
916     /**
917      * Computes the location and extent of each child view
918      * in this <code>BoxView</code> given the <code>targetSpan</code>,
919      * which is the width (or height) of the region we have to
920      * work with.
921      *
922      * @param targetSpan the total span given to the view, which
923      * would be used to layout the children
924      * @param axis the axis being studied, either
925      * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
926      * @param offsets an empty array filled by this method with
927      * values specifying the location of each child view
928      * @param spans an empty array filled by this method with
929      * values specifying the extent of each child view
930      */

931     protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
932         int totalAscent = (int)(targetSpan * getAlignment(axis));
933         int totalDescent = targetSpan - totalAscent;
934
935         int n = getViewCount();
936
937         for (int i = 0; i < n; i++) {
938             View JavaDoc v = getView(i);
939             float align = v.getAlignment(axis);
940             int viewSpan;
941
942             if (v.getResizeWeight(axis) > 0) {
943                 // if resizable then resize to the best fit
944

945                 // the smallest span possible
946
int minSpan = (int)v.getMinimumSpan(axis);
947                 // the largest span possible
948
int maxSpan = (int)v.getMaximumSpan(axis);
949
950                 if (align == 0.0f) {
951                     // if the alignment is 0 then we need to fit into the descent
952
viewSpan = Math.max(Math.min(maxSpan, totalDescent), minSpan);
953                 } else if (align == 1.0f) {
954                     // if the alignment is 1 then we need to fit into the ascent
955
viewSpan = Math.max(Math.min(maxSpan, totalAscent), minSpan);
956                 } else {
957                     // figure out the span that we must fit into
958
int fitSpan = (int)Math.min(totalAscent / align,
959                                                 totalDescent / (1.0f - align));
960                     // fit into the calculated span
961
viewSpan = Math.max(Math.min(maxSpan, fitSpan), minSpan);
962                 }
963             } else {
964                 // otherwise use the preferred spans
965
viewSpan = (int)v.getPreferredSpan(axis);
966             }
967
968             offsets[i] = totalAscent - (int)(viewSpan * align);
969             spans[i] = viewSpan;
970         }
971     }
972
973     /**
974      * Calculates the size requirements for this <code>BoxView</code>
975      * by examining the size of each child view.
976      *
977      * @param axis the axis being studied
978      * @param r the <code>SizeRequirements</code> object;
979      * if <code>null</code> one will be created
980      * @return the newly initialized <code>SizeRequirements</code> object
981      */

982     protected SizeRequirements JavaDoc baselineRequirements(int axis, SizeRequirements JavaDoc r) {
983         SizeRequirements JavaDoc totalAscent = new SizeRequirements JavaDoc();
984         SizeRequirements JavaDoc totalDescent = new SizeRequirements JavaDoc();
985         
986         if (r == null) {
987             r = new SizeRequirements JavaDoc();
988         }
989         
990         r.alignment = 0.5f;
991
992         int n = getViewCount();
993
994         // loop through all children calculating the max of all their ascents and
995
// descents at minimum, preferred, and maximum sizes
996
for (int i = 0; i < n; i++) {
997             View JavaDoc v = getView(i);
998             float align = v.getAlignment(axis);
999             int span;
1000            int ascent;
1001            int descent;
1002
1003            // find the maximum of the preferred ascents and descents
1004
span = (int)v.getPreferredSpan(axis);
1005            ascent = (int)(align * span);
1006            descent = span - ascent;
1007            totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
1008            totalDescent.preferred = Math.max(descent, totalDescent.preferred);
1009            
1010            if (v.getResizeWeight(axis) > 0) {
1011                // if the view is resizable then do the same for the minimum and
1012
// maximum ascents and descents
1013
span = (int)v.getMinimumSpan(axis);
1014                ascent = (int)(align * span);
1015                descent = span - ascent;
1016                totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
1017                totalDescent.minimum = Math.max(descent, totalDescent.minimum);
1018
1019                span = (int)v.getMaximumSpan(axis);
1020                ascent = (int)(align * span);
1021                descent = span - ascent;
1022                totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
1023                totalDescent.maximum = Math.max(descent, totalDescent.maximum);
1024            } else {
1025                // otherwise use the preferred
1026
totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
1027                totalDescent.minimum = Math.max(descent, totalDescent.minimum);
1028                totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
1029                totalDescent.maximum = Math.max(descent, totalDescent.maximum);
1030            }
1031        }
1032        
1033        // we now have an overall preferred, minimum, and maximum ascent and descent
1034

1035        // calculate the preferred span as the sum of the preferred ascent and preferred descent
1036
r.preferred = (int)Math.min((long)totalAscent.preferred + (long)totalDescent.preferred,
1037                                    Integer.MAX_VALUE);
1038
1039        // calculate the preferred alignment as the preferred ascent divided by the preferred span
1040
if (r.preferred > 0) {
1041            r.alignment = (float)totalAscent.preferred / r.preferred;
1042        }
1043        
1044
1045        if (r.alignment == 0.0f) {
1046            // if the preferred alignment is 0 then the minimum and maximum spans are simply
1047
// the minimum and maximum descents since there's nothing above the baseline
1048
r.minimum = totalDescent.minimum;
1049            r.maximum = totalDescent.maximum;
1050        } else if (r.alignment == 1.0f) {
1051            // if the preferred alignment is 1 then the minimum and maximum spans are simply
1052
// the minimum and maximum ascents since there's nothing below the baseline
1053
r.minimum = totalAscent.minimum;
1054            r.maximum = totalAscent.maximum;
1055        } else {
1056            // we want to honor the preferred alignment so we calculate two possible minimum
1057
// span values using 1) the minimum ascent and the alignment, and 2) the minimum
1058
// descent and the alignment. We'll choose the larger of these two numbers.
1059
r.minimum = Math.max((int)(totalAscent.minimum / r.alignment),
1060                                 (int)(totalDescent.minimum / (1.0f - r.alignment)));
1061            // a similar calculation is made for the maximum but we choose the smaller number.
1062
r.maximum = Math.min((int)(totalAscent.maximum / r.alignment),
1063                                 (int)(totalDescent.maximum / (1.0f - r.alignment)));
1064        }
1065
1066        return r;
1067    }
1068
1069    /**
1070     * Fetches the offset of a particular child's current layout.
1071     * @param axis the axis being studied
1072     * @param childIndex the index of the requested child
1073     * @return the offset (location) for the specified child
1074     */

1075    protected int getOffset(int axis, int childIndex) {
1076    int[] offsets = (axis == majorAxis) ? majorOffsets : minorOffsets;
1077    return offsets[childIndex];
1078    }
1079
1080    /**
1081     * Fetches the span of a particular childs current layout.
1082     * @param axis the axis being studied
1083     * @param childIndex the index of the requested child
1084     * @return the span (width or height) of the specified child
1085     */

1086    protected int getSpan(int axis, int childIndex) {
1087    int[] spans = (axis == majorAxis) ? majorSpans : minorSpans;
1088    return spans[childIndex];
1089    }
1090
1091    /**
1092     * Determines in which direction the next view lays.
1093     * Consider the View at index n. Typically the <code>View</code>s
1094     * are layed out from left to right, so that the <code>View</code>
1095     * to the EAST will be at index n + 1, and the <code>View</code>
1096     * to the WEST will be at index n - 1. In certain situations,
1097     * such as with bidirectional text, it is possible
1098     * that the <code>View</code> to EAST is not at index n + 1,
1099     * but rather at index n - 1, or that the <code>View</code>
1100     * to the WEST is not at index n - 1, but index n + 1.
1101     * In this case this method would return true,
1102     * indicating the <code>View</code>s are layed out in
1103     * descending order. Otherwise the method would return false
1104     * indicating the <code>View</code>s are layed out in ascending order.
1105     * <p>
1106     * If the receiver is laying its <code>View</code>s along the
1107     * <code>Y_AXIS</code>, this will will return the value from
1108     * invoking the same method on the <code>View</code>
1109     * responsible for rendering <code>position</code> and
1110     * <code>bias</code>. Otherwise this will return false.
1111     *
1112     * @param position position into the model
1113     * @param bias either <code>Position.Bias.Forward</code> or
1114     * <code>Position.Bias.Backward</code>
1115     * @return true if the <code>View</code>s surrounding the
1116     * <code>View</code> responding for rendering
1117     * <code>position</code> and <code>bias</code>
1118     * are layed out in descending order; otherwise false
1119     */

1120    protected boolean flipEastAndWestAtEnds(int position,
1121                        Position.Bias JavaDoc bias) {
1122    if(majorAxis == Y_AXIS) {
1123        int testPos = (bias == Position.Bias.Backward) ?
1124                  Math.max(0, position - 1) : position;
1125        int index = getViewIndexAtPosition(testPos);
1126        if(index != -1) {
1127        View JavaDoc v = getView(index);
1128        if(v != null && v instanceof CompositeView JavaDoc) {
1129            return ((CompositeView JavaDoc)v).flipEastAndWestAtEnds(position,
1130                                    bias);
1131        }
1132        }
1133    }
1134    return false;
1135    }
1136
1137    // --- variables ------------------------------------------------
1138

1139    int majorAxis;
1140
1141    int majorSpan;
1142    int minorSpan;
1143
1144    /*
1145     * Request cache
1146     */

1147    boolean majorReqValid;
1148    boolean minorReqValid;
1149    SizeRequirements JavaDoc majorRequest;
1150    SizeRequirements JavaDoc minorRequest;
1151
1152    /*
1153     * Allocation cache
1154     */

1155    boolean majorAllocValid;
1156    int[] majorOffsets;
1157    int[] majorSpans;
1158    boolean minorAllocValid;
1159    int[] minorOffsets;
1160    int[] minorSpans;
1161
1162    /** used in paint. */
1163    Rectangle tempRect;
1164}
1165
Popular Tags