Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / javax / swing / text / AsyncBoxView.java
blob90447f86e6ad89d1784c988f51847cb69ee0b6e5
1 /* AsyncBoxView.java -- A box view that performs layout asynchronously
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text;
41 import java.awt.Component;
42 import java.awt.Graphics;
43 import java.awt.Rectangle;
44 import java.awt.Shape;
45 import java.util.ArrayList;
47 import javax.swing.event.DocumentEvent;
48 import javax.swing.text.Position.Bias;
50 /**
51 * A {@link View} implementation that lays out its child views in a box, either
52 * vertically or horizontally. The difference to {@link BoxView} is that the
53 * layout is performed in an asynchronous manner. This helps to keep the
54 * eventqueue free from non-GUI related tasks.
56 * This view is currently not used in standard text components. In order to
57 * use it you would have to implement a special {@link EditorKit} with a
58 * {@link ViewFactory} that returns this view. For example:
60 * <pre>
61 * static class AsyncEditorKit extends StyledEditorKit implements ViewFactory
62 * {
63 * public View create(Element el)
64 * {
65 * if (el.getName().equals(AbstractDocument.SectionElementName))
66 * return new AsyncBoxView(el, View.Y_AXIS);
67 * return super.getViewFactory().create(el);
68 * }
69 * public ViewFactory getViewFactory() {
70 * return this;
71 * }
72 * }
73 * </pre>
75 * @author Roman Kennke (kennke@aicas.com)
77 * @since 1.3
79 public class AsyncBoxView
80 extends View
83 /**
84 * Manages the effective position of child views. That keeps the visible
85 * layout stable while the AsyncBoxView might be changing until the layout
86 * thread decides to publish the new layout.
88 public class ChildLocator
91 /**
92 * The last valid location.
94 protected ChildState lastValidOffset;
96 /**
97 * The last allocation.
99 protected Rectangle lastAlloc;
102 * A Rectangle used for child allocation calculation to avoid creation
103 * of lots of garbage Rectangle objects.
105 protected Rectangle childAlloc;
108 * Creates a new ChildLocator.
110 public ChildLocator()
112 lastAlloc = new Rectangle();
113 childAlloc = new Rectangle();
117 * Receives notification that a child has changed. This is called by
118 * child state objects that have changed it's major span.
120 * This sets the {@link #lastValidOffset} field to <code>cs</code> if
121 * the new child state's view start offset is smaller than the start offset
122 * of the current child state's view or when <code>lastValidOffset</code>
123 * is <code>null</code>.
125 * @param cs the child state object that has changed
127 public synchronized void childChanged(ChildState cs)
129 if (lastValidOffset == null
130 || cs.getChildView().getStartOffset()
131 < lastValidOffset.getChildView().getStartOffset())
133 lastValidOffset = cs;
138 * Returns the view index of the view that occupies the specified area, or
139 * <code>-1</code> if there is no such child view.
141 * @param x the x coordinate (relative to <code>a</code>)
142 * @param y the y coordinate (relative to <code>a</code>)
143 * @param a the current allocation of this view
145 * @return the view index of the view that occupies the specified area, or
146 * <code>-1</code> if there is no such child view
148 public int getViewIndexAtPoint(float x, float y, Shape a)
150 setAllocation(a);
151 float targetOffset = (getMajorAxis() == X_AXIS) ? x - lastAlloc.x
152 : y - lastAlloc.y;
153 int index = getViewIndexAtVisualOffset(targetOffset);
154 return index;
158 * Returns the current allocation for a child view. This updates the
159 * offsets for all children <em>before</em> the requested child view.
161 * @param index the index of the child view
162 * @param a the current allocation of this view
164 * @return the current allocation for a child view
166 public synchronized Shape getChildAllocation(int index, Shape a)
168 if (a == null)
169 return null;
170 setAllocation(a);
171 ChildState cs = getChildState(index);
172 if (cs.getChildView().getStartOffset()
173 > lastValidOffset.getChildView().getStartOffset())
175 updateChildOffsetsToIndex(index);
177 Shape ca = getChildAllocation(index);
178 return ca;
182 * Paints all child views.
184 * @param g the graphics context to use
186 public synchronized void paintChildren(Graphics g)
188 Rectangle clip = g.getClipBounds();
189 float targetOffset = (getMajorAxis() == X_AXIS) ? clip.x - lastAlloc.x
190 : clip.y - lastAlloc.y;
191 int index = getViewIndexAtVisualOffset(targetOffset);
192 int n = getViewCount();
193 float offs = getChildState(index).getMajorOffset();
194 for (int i = index; i < n; i++)
196 ChildState cs = getChildState(i);
197 cs.setMajorOffset(offs);
198 Shape ca = getChildAllocation(i);
199 if (ca.intersects(clip))
201 synchronized (cs)
203 View v = cs.getChildView();
204 v.paint(g, ca);
207 else
209 // done painting intersection
210 break;
212 offs += cs.getMajorSpan();
217 * Returns the current allocation of the child view with the specified
218 * index. Note that this will <em>not</em> update any location information.
220 * @param index the index of the requested child view
222 * @return the current allocation of the child view with the specified
223 * index
225 protected Shape getChildAllocation(int index)
227 ChildState cs = getChildState(index);
228 if (! cs.isLayoutValid())
229 cs.run();
231 if (getMajorAxis() == X_AXIS)
233 childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset();
234 childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset();
235 childAlloc.width = (int) cs.getMajorSpan();
236 childAlloc.height = (int) cs.getMinorSpan();
238 else
240 childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset();
241 childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset();
242 childAlloc.height = (int) cs.getMajorSpan();
243 childAlloc.width = (int) cs.getMinorSpan();
245 return childAlloc;
249 * Sets the current allocation for this view.
251 * @param a the allocation to set
253 protected void setAllocation(Shape a)
255 if (a instanceof Rectangle)
256 lastAlloc.setBounds((Rectangle) a);
257 else
258 lastAlloc.setBounds(a.getBounds());
260 setSize(lastAlloc.width, lastAlloc.height);
264 * Returns the index of the view at the specified offset along the major
265 * layout axis.
267 * @param targetOffset the requested offset
269 * @return the index of the view at the specified offset along the major
270 * layout axis
272 protected int getViewIndexAtVisualOffset(float targetOffset)
274 int n = getViewCount();
275 if (n > 0)
277 if (lastValidOffset == null)
278 lastValidOffset = getChildState(0);
279 if (targetOffset > majorSpan)
280 return 0;
281 else if (targetOffset > lastValidOffset.getMajorOffset())
282 return updateChildOffsets(targetOffset);
283 else
285 float offs = 0f;
286 for (int i = 0; i < n; i++)
288 ChildState cs = getChildState(i);
289 float nextOffs = offs + cs.getMajorSpan();
290 if (targetOffset < nextOffs)
291 return i;
292 offs = nextOffs;
296 return n - 1;
300 * Updates all the child view offsets up to the specified targetOffset.
302 * @param targetOffset the offset up to which the child view offsets are
303 * updated
305 * @return the index of the view at the specified offset
307 private int updateChildOffsets(float targetOffset)
309 int n = getViewCount();
310 int targetIndex = n - 1;;
311 int pos = lastValidOffset.getChildView().getStartOffset();
312 int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
313 float start = lastValidOffset.getMajorOffset();
314 float lastOffset = start;
315 for (int i = startIndex; i < n; i++)
317 ChildState cs = getChildState(i);
318 cs.setMajorOffset(lastOffset);
319 lastOffset += cs.getMajorSpan();
320 if (targetOffset < lastOffset)
322 targetIndex = i;
323 lastValidOffset = cs;
324 break;
327 return targetIndex;
331 * Updates the offsets of the child views up to the specified index.
333 * @param index the index up to which the offsets are updated
335 private void updateChildOffsetsToIndex(int index)
337 int pos = lastValidOffset.getChildView().getStartOffset();
338 int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
339 float lastOffset = lastValidOffset.getMajorOffset();
340 for (int i = startIndex; i <= index; i++)
342 ChildState cs = getChildState(i);
343 cs.setMajorOffset(lastOffset);
344 lastOffset += cs.getMajorSpan();
350 * Represents the layout state of a child view.
352 public class ChildState
353 implements Runnable
357 * The child view for this state record.
359 private View childView;
362 * Indicates if the minor axis requirements of this child view are valid
363 * or not.
365 private boolean minorValid;
368 * Indicates if the major axis requirements of this child view are valid
369 * or not.
371 private boolean majorValid;
374 * Indicates if the current child size is valid. This is package private
375 * to avoid synthetic accessor method.
377 boolean childSizeValid;
380 * The child views minimumSpan. This is package private to avoid accessor
381 * method.
383 float minimum;
386 * The child views preferredSpan. This is package private to avoid accessor
387 * method.
389 float preferred;
392 * The current span of the child view along the major axis.
394 private float majorSpan;
397 * The current offset of the child view along the major axis.
399 private float majorOffset;
402 * The current span of the child view along the minor axis.
404 private float minorSpan;
407 * The current offset of the child view along the major axis.
409 private float minorOffset;
412 * The child views maximumSpan.
414 private float maximum;
417 * Creates a new <code>ChildState</code> object for the specified child
418 * view.
420 * @param view the child view for which to create the state record
422 public ChildState(View view)
424 childView = view;
428 * Returns the child view for which this <code>ChildState</code> represents
429 * the layout state.
431 * @return the child view for this child state object
433 public View getChildView()
435 return childView;
439 * Returns <code>true</code> if the current layout information is valid,
440 * <code>false</code> otherwise.
442 * @return <code>true</code> if the current layout information is valid,
443 * <code>false</code> otherwise
445 public boolean isLayoutValid()
447 return minorValid && majorValid && childSizeValid;
451 * Performs the layout update for the child view managed by this
452 * <code>ChildState</code>.
454 public void run()
456 Document doc = getDocument();
457 if (doc instanceof AbstractDocument)
459 AbstractDocument abstractDoc = (AbstractDocument) doc;
460 abstractDoc.readLock();
466 if (!(minorValid && majorValid && childSizeValid)
467 && childView.getParent() == AsyncBoxView.this)
469 synchronized(AsyncBoxView.this)
471 changing = this;
473 update();
474 synchronized(AsyncBoxView.this)
476 changing = null;
478 // Changing the major axis may cause the minor axis
479 // requirements to have changed, so we need to do this again.
480 update();
483 finally
485 if (doc instanceof AbstractDocument)
487 AbstractDocument abstractDoc = (AbstractDocument) doc;
488 abstractDoc.readUnlock();
494 * Performs the actual update after the run methods has made its checks
495 * and locked the document.
497 private void update()
499 int majorAxis = getMajorAxis();
500 boolean minorUpdated = false;
501 synchronized (this)
503 if (! minorValid)
505 int minorAxis = getMinorAxis();
506 minimum = childView.getMinimumSpan(minorAxis);
507 preferred = childView.getPreferredSpan(minorAxis);
508 maximum = childView.getMaximumSpan(minorAxis);
509 minorValid = true;
510 minorUpdated = true;
513 if (minorUpdated)
514 minorRequirementChange(this);
516 boolean majorUpdated = false;
517 float delta = 0.0F;
518 synchronized (this)
520 if (! majorValid)
522 float oldSpan = majorSpan;
523 majorSpan = childView.getPreferredSpan(majorAxis);
524 delta = majorSpan - oldSpan;
525 majorValid = true;
526 majorUpdated = true;
529 if (majorUpdated)
531 majorRequirementChange(this, delta);
532 locator.childChanged(this);
535 synchronized (this)
537 if (! childSizeValid)
539 float w;
540 float h;
541 if (majorAxis == X_AXIS)
543 w = majorSpan;
544 h = getMinorSpan();
546 else
548 w = getMinorSpan();
549 h = majorSpan;
551 childSizeValid = true;
552 childView.setSize(w, h);
558 * Returns the span of the child view along the minor layout axis.
560 * @return the span of the child view along the minor layout axis
562 public float getMinorSpan()
564 float retVal;
565 if (maximum < minorSpan)
566 retVal = maximum;
567 else
568 retVal = Math.max(minimum, minorSpan);
569 return retVal;
573 * Returns the offset of the child view along the minor layout axis.
575 * @return the offset of the child view along the minor layout axis
577 public float getMinorOffset()
579 float retVal;
580 if (maximum < minorSpan)
582 float align = childView.getAlignment(getMinorAxis());
583 retVal = ((minorSpan - maximum) * align);
585 else
586 retVal = 0f;
588 return retVal;
592 * Returns the span of the child view along the major layout axis.
594 * @return the span of the child view along the major layout axis
597 public float getMajorSpan()
599 return majorSpan;
603 * Returns the offset of the child view along the major layout axis.
605 * @return the offset of the child view along the major layout axis
607 public float getMajorOffset()
609 return majorOffset;
613 * Sets the offset of the child view along the major layout axis. This
614 * should only be called by the ChildLocator of that child view.
616 * @param offset the offset to set
618 public void setMajorOffset(float offset)
620 majorOffset = offset;
624 * Mark the preferences changed for that child. This forwards to
625 * {@link AsyncBoxView#preferenceChanged}.
627 * @param width <code>true</code> if the width preference has changed
628 * @param height <code>true</code> if the height preference has changed
630 public void preferenceChanged(boolean width, boolean height)
632 if (getMajorAxis() == X_AXIS)
634 if (width)
635 majorValid = false;
636 if (height)
637 minorValid = false;
639 else
641 if (width)
642 minorValid = false;
643 if (height)
644 majorValid = false;
646 childSizeValid = false;
651 * Flushes the requirements changes upwards asynchronously.
653 private class FlushTask implements Runnable
656 * Starts the flush task. This obtains a readLock on the document
657 * and then flushes all the updates using
658 * {@link AsyncBoxView#flushRequirementChanges()} after updating the
659 * requirements.
661 public void run()
665 // Acquire a lock on the document.
666 Document doc = getDocument();
667 if (doc instanceof AbstractDocument)
669 AbstractDocument abstractDoc = (AbstractDocument) doc;
670 abstractDoc.readLock();
673 int n = getViewCount();
674 if (minorChanged && (n > 0))
676 LayoutQueue q = getLayoutQueue();
677 ChildState min = getChildState(0);
678 ChildState pref = getChildState(0);
679 for (int i = 1; i < n; i++)
681 ChildState cs = getChildState(i);
682 if (cs.minimum > min.minimum)
683 min = cs;
684 if (cs.preferred > pref.preferred)
685 pref = cs;
687 synchronized (AsyncBoxView.this)
689 minReq = min;
690 prefReq = pref;
694 flushRequirementChanges();
696 finally
698 // Release the lock on the document.
699 Document doc = getDocument();
700 if (doc instanceof AbstractDocument)
702 AbstractDocument abstractDoc = (AbstractDocument) doc;
703 abstractDoc.readUnlock();
711 * The major layout axis.
713 private int majorAxis;
716 * The top inset.
718 private float topInset;
721 * The bottom inset.
723 private float bottomInset;
726 * The left inset.
728 private float leftInset;
731 * Indicates if the major span should be treated as beeing estimated or not.
733 private boolean estimatedMajorSpan;
736 * The right inset.
738 private float rightInset;
741 * The children and their layout statistics.
743 private ArrayList childStates;
746 * The currently changing child state. May be null if there is no child state
747 * updating at the moment. This is package private to avoid a synthetic
748 * accessor method inside ChildState.
750 ChildState changing;
753 * Represents the minimum requirements. This is used in
754 * {@link #getMinimumSpan(int)}.
756 ChildState minReq;
759 * Represents the minimum requirements. This is used in
760 * {@link #getPreferredSpan(int)}.
762 ChildState prefReq;
765 * Indicates that the major axis requirements have changed.
767 private boolean majorChanged;
770 * Indicates that the minor axis requirements have changed. This is package
771 * private to avoid synthetic accessor method.
773 boolean minorChanged;
776 * The current span along the major layout axis. This is package private to
777 * avoid synthetic accessor method.
779 float majorSpan;
782 * The current span along the minor layout axis. This is package private to
783 * avoid synthetic accessor method.
785 float minorSpan;
788 * This tasked is placed on the layout queue to flush updates up to the
789 * parent view.
791 private Runnable flushTask;
794 * The child locator for this view.
796 protected ChildLocator locator;
799 * Creates a new <code>AsyncBoxView</code> that represents the specified
800 * element and layouts its children along the specified axis.
802 * @param elem the element
803 * @param axis the layout axis
805 public AsyncBoxView(Element elem, int axis)
807 super(elem);
808 majorAxis = axis;
809 childStates = new ArrayList();
810 flushTask = new FlushTask();
811 locator = new ChildLocator();
812 minorSpan = Short.MAX_VALUE;
816 * Returns the major layout axis.
818 * @return the major layout axis
820 public int getMajorAxis()
822 return majorAxis;
826 * Returns the minor layout axis, that is the axis orthogonal to the major
827 * layout axis.
829 * @return the minor layout axis
831 public int getMinorAxis()
833 return majorAxis == X_AXIS ? Y_AXIS : X_AXIS;
837 * Returns the view at the specified <code>index</code>.
839 * @param index the index of the requested child view
841 * @return the view at the specified <code>index</code>
843 public View getView(int index)
845 View view = null;
846 synchronized(childStates)
848 if ((index >= 0) && (index < childStates.size()))
850 ChildState cs = (ChildState) childStates.get(index);
851 view = cs.getChildView();
854 return view;
858 * Returns the number of child views.
860 * @return the number of child views
862 public int getViewCount()
864 synchronized(childStates)
866 return childStates.size();
871 * Returns the view index of the child view that represents the specified
872 * model position.
874 * @param pos the model position for which we search the view index
875 * @param bias the bias
877 * @return the view index of the child view that represents the specified
878 * model position
880 public int getViewIndex(int pos, Position.Bias bias)
882 int retVal = -1;
884 if (bias == Position.Bias.Backward)
885 pos = Math.max(0, pos - 1);
887 // TODO: A possible optimization would be to implement a binary search
888 // here.
889 int numChildren = childStates.size();
890 if (numChildren > 0)
892 for (int i = 0; i < numChildren; ++i)
894 View child = ((ChildState) childStates.get(i)).getChildView();
895 if (child.getStartOffset() <= pos && child.getEndOffset() > pos)
897 retVal = i;
898 break;
902 return retVal;
906 * Returns the top inset.
908 * @return the top inset
910 public float getTopInset()
912 return topInset;
916 * Sets the top inset.
918 * @param top the top inset
920 public void setTopInset(float top)
922 topInset = top;
926 * Returns the bottom inset.
928 * @return the bottom inset
930 public float getBottomInset()
932 return bottomInset;
936 * Sets the bottom inset.
938 * @param bottom the bottom inset
940 public void setBottomInset(float bottom)
942 bottomInset = bottom;
946 * Returns the left inset.
948 * @return the left inset
950 public float getLeftInset()
952 return leftInset;
956 * Sets the left inset.
958 * @param left the left inset
960 public void setLeftInset(float left)
962 leftInset = left;
966 * Returns the right inset.
968 * @return the right inset
970 public float getRightInset()
972 return rightInset;
976 * Sets the right inset.
978 * @param right the right inset
980 public void setRightInset(float right)
982 rightInset = right;
986 * Loads the child views of this view. This is triggered by
987 * {@link #setParent(View)}.
989 * @param f the view factory to build child views with
991 protected void loadChildren(ViewFactory f)
993 Element e = getElement();
994 int n = e.getElementCount();
995 if (n > 0)
997 View[] added = new View[n];
998 for (int i = 0; i < n; i++)
1000 added[i] = f.create(e.getElement(i));
1002 replace(0, 0, added);
1007 * Returns the span along an axis that is taken up by the insets.
1009 * @param axis the axis
1011 * @return the span along an axis that is taken up by the insets
1013 * @since 1.4
1015 protected float getInsetSpan(int axis)
1017 float span;
1018 if (axis == X_AXIS)
1019 span = leftInset + rightInset;
1020 else
1021 span = topInset + bottomInset;
1022 return span;
1026 * Sets the <code>estimatedMajorSpan</code> property that determines if
1027 * the major span should be treated as beeing estimated.
1029 * @param estimated if the major span should be treated as estimated or not
1031 * @since 1.4
1033 protected void setEstimatedMajorSpan(boolean estimated)
1035 estimatedMajorSpan = estimated;
1039 * Determines whether the major span should be treated as estimated or as
1040 * beeing accurate.
1042 * @return <code>true</code> if the major span should be treated as
1043 * estimated, <code>false</code> if the major span should be treated
1044 * as accurate
1046 * @since 1.4
1048 protected boolean getEstimatedMajorSpan()
1050 return estimatedMajorSpan;
1054 * Receives notification from the child states that the requirements along
1055 * the minor axis have changed.
1057 * @param cs the child state from which this notification is messaged
1059 protected synchronized void minorRequirementChange(ChildState cs)
1061 minorChanged = true;
1065 * Receives notification from the child states that the requirements along
1066 * the major axis have changed.
1068 * @param cs the child state from which this notification is messaged
1070 protected void majorRequirementChange(ChildState cs, float delta)
1072 if (! estimatedMajorSpan)
1073 majorSpan += delta;
1074 majorChanged = true;
1078 * Sets the parent for this view. This calls loadChildren if
1079 * <code>parent</code> is not <code>null</code> and there have not been any
1080 * child views initializes.
1082 * @param parent the new parent view; <code>null</code> if this view is
1083 * removed from the view hierarchy
1085 * @see View#setParent(View)
1087 public void setParent(View parent)
1089 super.setParent(parent);
1090 if ((parent != null) && (getViewCount() == 0))
1092 ViewFactory f = getViewFactory();
1093 loadChildren(f);
1098 * Sets the size of this view. This is ususally called before {@link #paint}
1099 * is called to make sure the view has a valid layout.
1101 * This implementation queues layout requests for every child view if the
1102 * minor axis span has changed. (The major axis span is requested to never
1103 * change for this view).
1105 * @param width the width of the view
1106 * @param height the height of the view
1108 public void setSize(float width, float height)
1110 float targetSpan;
1111 if (majorAxis == X_AXIS)
1112 targetSpan = height - getTopInset() - getBottomInset();
1113 else
1114 targetSpan = width - getLeftInset() - getRightInset();
1116 if (targetSpan != minorSpan)
1118 minorSpan = targetSpan;
1120 int n = getViewCount();
1121 LayoutQueue q = getLayoutQueue();
1122 for (int i = 0; i < n; i++)
1124 ChildState cs = getChildState(i);
1125 cs.childSizeValid = false;
1126 q.addTask(cs);
1128 q.addTask(flushTask);
1133 * Replaces child views with new child views.
1135 * This creates ChildState objects for all the new views and adds layout
1136 * requests for them to the layout queue.
1138 * @param offset the offset at which to remove/insert
1139 * @param length the number of child views to remove
1140 * @param views the new child views to insert
1142 public void replace(int offset, int length, View[] views)
1144 synchronized(childStates)
1146 LayoutQueue q = getLayoutQueue();
1147 for (int i = 0; i < length; i++)
1148 childStates.remove(offset);
1150 for (int i = views.length - 1; i >= 0; i--)
1151 childStates.add(offset, createChildState(views[i]));
1153 // We need to go through the new child states _after_ they have been
1154 // added to the childStates list, otherwise the layout tasks may find
1155 // an incomplete child list. That means we have to loop through
1156 // them again, but what else can we do?
1157 if (views.length != 0)
1159 for (int i = 0; i < views.length; i++)
1161 ChildState cs = (ChildState) childStates.get(i + offset);
1162 cs.getChildView().setParent(this);
1163 q.addTask(cs);
1165 q.addTask(flushTask);
1171 * Paints the view. This requests the {@link ChildLocator} to paint the views
1172 * after setting the allocation on it.
1174 * @param g the graphics context to use
1175 * @param s the allocation for this view
1177 public void paint(Graphics g, Shape s)
1179 synchronized (locator)
1181 locator.setAllocation(s);
1182 locator.paintChildren(g);
1187 * Returns the preferred span of this view along the specified layout axis.
1189 * @return the preferred span of this view along the specified layout axis
1191 public float getPreferredSpan(int axis)
1193 float retVal;
1194 if (majorAxis == axis)
1195 retVal = majorSpan;
1197 else if (prefReq != null)
1199 View child = prefReq.getChildView();
1200 retVal = child.getPreferredSpan(axis);
1203 // If we have no layout information yet, then return insets + 30 as
1204 // an estimation.
1205 else
1207 if (axis == X_AXIS)
1208 retVal = getLeftInset() + getRightInset() + 30;
1209 else
1210 retVal = getTopInset() + getBottomInset() + 30;
1212 return retVal;
1216 * Maps a model location to view coordinates.
1218 * @param pos the model location
1219 * @param a the current allocation of this view
1220 * @param b the bias
1222 * @return the view allocation for the specified model location
1224 public Shape modelToView(int pos, Shape a, Bias b)
1225 throws BadLocationException
1227 int index = getViewIndexAtPosition(pos, b);
1228 Shape ca = locator.getChildAllocation(index, a);
1230 ChildState cs = getChildState(index);
1231 synchronized (cs)
1233 View cv = cs.getChildView();
1234 Shape v = cv.modelToView(pos, ca, b);
1235 return v;
1240 * Maps view coordinates to a model location.
1242 * @param x the x coordinate (relative to <code>a</code>)
1243 * @param y the y coordinate (relative to <code>a</code>)
1244 * @param b holds the bias of the model location on method exit
1246 * @return the model location for the specified view location
1248 public int viewToModel(float x, float y, Shape a, Bias[] b)
1250 int pos;
1251 int index;
1252 Shape ca;
1254 synchronized (locator)
1256 index = locator.getViewIndexAtPoint(x, y, a);
1257 ca = locator.getChildAllocation(index, a);
1260 ChildState cs = getChildState(index);
1261 synchronized (cs)
1263 View v = cs.getChildView();
1264 pos = v.viewToModel(x, y, ca, b);
1266 return pos;
1270 * Returns the child allocation for the child view with the specified
1271 * <code>index</code>.
1273 * @param index the index of the child view
1274 * @param a the current allocation of this view
1276 * @return the allocation of the child view
1278 public Shape getChildAllocation(int index, Shape a)
1280 Shape ca = locator.getChildAllocation(index, a);
1281 return ca;
1285 * Returns the maximum span of this view along the specified axis.
1286 * This is implemented to return the <code>preferredSpan</code> for the
1287 * major axis (that means the box can't be resized along the major axis) and
1288 * {@link Short#MAX_VALUE} for the minor axis.
1290 * @param axis the axis
1292 * @return the maximum span of this view along the specified axis
1294 public float getMaximumSpan(int axis)
1296 float max;
1297 if (axis == majorAxis)
1298 max = getPreferredSpan(axis);
1299 else
1300 max = Short.MAX_VALUE;
1301 return max;
1305 * Returns the minimum span along the specified axis.
1307 public float getMinimumSpan(int axis)
1309 float min;
1310 if (axis == majorAxis)
1311 min = getPreferredSpan(axis);
1312 else
1314 if (minReq != null)
1316 View child = minReq.getChildView();
1317 min = child.getMinimumSpan(axis);
1319 else
1321 // No layout information yet. Return insets + 5 as some kind of
1322 // estimation.
1323 if (axis == X_AXIS)
1324 min = getLeftInset() + getRightInset() + 5;
1325 else
1326 min = getTopInset() + getBottomInset() + 5;
1329 return min;
1333 * Receives notification that one of the child views has changed its
1334 * layout preferences along one or both axis.
1336 * This queues a layout request for that child view if necessary.
1338 * @param view the view that has changed its preferences
1339 * @param width <code>true</code> if the width preference has changed
1340 * @param height <code>true</code> if the height preference has changed
1342 public synchronized void preferenceChanged(View view, boolean width,
1343 boolean height)
1345 if (view == null)
1346 getParent().preferenceChanged(this, width, height);
1347 else
1349 if (changing != null)
1351 View cv = changing.getChildView();
1352 if (cv == view)
1354 changing.preferenceChanged(width, height);
1355 return;
1358 int index = getViewIndexAtPosition(view.getStartOffset(),
1359 Position.Bias.Forward);
1360 ChildState cs = getChildState(index);
1361 cs.preferenceChanged(width, height);
1362 LayoutQueue q = getLayoutQueue();
1363 q.addTask(cs);
1364 q.addTask(flushTask);
1369 * Updates the layout for this view. This is implemented to trigger
1370 * {@link ChildLocator#childChanged} for the changed view, if there is
1371 * any.
1373 * @param ec the element change, may be <code>null</code> if there were
1374 * no changes to the element of this view
1375 * @param e the document event
1376 * @param a the current allocation of this view
1378 protected void updateLayout(DocumentEvent.ElementChange ec,
1379 DocumentEvent e, Shape a)
1381 if (ec != null)
1383 int index = Math.max(ec.getIndex() - 1, 0);
1384 ChildState cs = getChildState(index);
1385 locator.childChanged(cs);
1391 * Returns the <code>ChildState</code> object associated with the child view
1392 * at the specified <code>index</code>.
1394 * @param index the index of the child view for which to query the state
1396 * @return the child state for the specified child view
1398 protected ChildState getChildState(int index) {
1399 synchronized (childStates)
1401 return (ChildState) childStates.get(index);
1406 * Returns the <code>LayoutQueue</code> used for layouting the box view.
1407 * This simply returns {@link LayoutQueue#getDefaultQueue()}.
1409 * @return the <code>LayoutQueue</code> used for layouting the box view
1411 protected LayoutQueue getLayoutQueue()
1413 return LayoutQueue.getDefaultQueue();
1417 * Returns the child view index of the view that represents the specified
1418 * position in the document model.
1420 * @param pos the position in the model
1421 * @param b the bias
1423 * @return the child view index of the view that represents the specified
1424 * position in the document model
1426 protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b)
1428 if (b == Position.Bias.Backward)
1429 pos = Math.max(0, pos - 1);
1430 Element elem = getElement();
1431 return elem.getElementIndex(pos);
1435 * Creates a <code>ChildState</code> object for the specified view.
1437 * @param v the view for which to create a child state object
1439 * @return the created child state
1441 protected ChildState createChildState(View v)
1443 return new ChildState(v);
1447 * Flushes the requirements changes upwards to the parent view. This is
1448 * called from the layout thread.
1450 protected synchronized void flushRequirementChanges()
1452 if (majorChanged || minorChanged)
1454 View p = getParent();
1455 if (p != null)
1457 boolean horizontal;
1458 boolean vertical;
1459 if (majorAxis == X_AXIS)
1461 horizontal = majorChanged;
1462 vertical = minorChanged;
1464 else
1466 vertical = majorChanged;
1467 horizontal = minorChanged;
1470 p.preferenceChanged(this, horizontal, vertical);
1471 majorChanged = false;
1472 minorChanged = false;
1474 Component c = getContainer();
1475 if (c != null)
1476 c.repaint();