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)
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
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
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
;
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:
61 * static class AsyncEditorKit extends StyledEditorKit implements ViewFactory
63 * public View create(Element el)
65 * if (el.getName().equals(AbstractDocument.SectionElementName))
66 * return new AsyncBoxView(el, View.Y_AXIS);
67 * return super.getViewFactory().create(el);
69 * public ViewFactory getViewFactory() {
75 * @author Roman Kennke (kennke@aicas.com)
79 public class AsyncBoxView
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
92 * The last valid location.
94 protected ChildState lastValidOffset
;
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
)
151 float targetOffset
= (getMajorAxis() == X_AXIS
) ? x
- lastAlloc
.x
153 int index
= getViewIndexAtVisualOffset(targetOffset
);
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
)
171 ChildState cs
= getChildState(index
);
172 if (cs
.getChildView().getStartOffset()
173 > lastValidOffset
.getChildView().getStartOffset())
175 updateChildOffsetsToIndex(index
);
177 Shape ca
= getChildAllocation(index
);
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
))
203 View v
= cs
.getChildView();
209 // done painting intersection
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
225 protected Shape
getChildAllocation(int index
)
227 ChildState cs
= getChildState(index
);
228 if (! cs
.isLayoutValid())
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();
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();
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
);
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
267 * @param targetOffset the requested offset
269 * @return the index of the view at the specified offset along the major
272 protected int getViewIndexAtVisualOffset(float targetOffset
)
274 int n
= getViewCount();
277 if (lastValidOffset
== null)
278 lastValidOffset
= getChildState(0);
279 if (targetOffset
> majorSpan
)
281 else if (targetOffset
> lastValidOffset
.getMajorOffset())
282 return updateChildOffsets(targetOffset
);
286 for (int i
= 0; i
< n
; i
++)
288 ChildState cs
= getChildState(i
);
289 float nextOffs
= offs
+ cs
.getMajorSpan();
290 if (targetOffset
< nextOffs
)
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
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
)
323 lastValidOffset
= cs
;
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
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
365 private boolean minorValid
;
368 * Indicates if the major axis requirements of this child view are valid
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
386 * The child views preferredSpan. This is package private to avoid accessor
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
420 * @param view the child view for which to create the state record
422 public ChildState(View view
)
428 * Returns the child view for which this <code>ChildState</code> represents
431 * @return the child view for this child state object
433 public View
getChildView()
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>.
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)
474 synchronized(AsyncBoxView
.this)
478 // Changing the major axis may cause the minor axis
479 // requirements to have changed, so we need to do this again.
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;
505 int minorAxis
= getMinorAxis();
506 minimum
= childView
.getMinimumSpan(minorAxis
);
507 preferred
= childView
.getPreferredSpan(minorAxis
);
508 maximum
= childView
.getMaximumSpan(minorAxis
);
514 minorRequirementChange(this);
516 boolean majorUpdated
= false;
522 float oldSpan
= majorSpan
;
523 majorSpan
= childView
.getPreferredSpan(majorAxis
);
524 delta
= majorSpan
- oldSpan
;
531 majorRequirementChange(this, delta
);
532 locator
.childChanged(this);
537 if (! childSizeValid
)
541 if (majorAxis
== X_AXIS
)
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()
565 if (maximum
< minorSpan
)
568 retVal
= Math
.max(minimum
, minorSpan
);
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()
580 if (maximum
< minorSpan
)
582 float align
= childView
.getAlignment(getMinorAxis());
583 retVal
= ((minorSpan
- maximum
) * align
);
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()
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()
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
)
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
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
)
684 if (cs
.preferred
> pref
.preferred
)
687 synchronized (AsyncBoxView
.this)
694 flushRequirementChanges();
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
;
718 private float topInset
;
723 private float bottomInset
;
728 private float leftInset
;
731 * Indicates if the major span should be treated as beeing estimated or not.
733 private boolean estimatedMajorSpan
;
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.
753 * Represents the minimum requirements. This is used in
754 * {@link #getMinimumSpan(int)}.
759 * Represents the minimum requirements. This is used in
760 * {@link #getPreferredSpan(int)}.
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.
782 * The current span along the minor layout axis. This is package private to
783 * avoid synthetic accessor method.
788 * This tasked is placed on the layout queue to flush updates up to the
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
)
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()
826 * Returns the minor layout axis, that is the axis orthogonal to the major
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
)
846 synchronized(childStates
)
848 if ((index
>= 0) && (index
< childStates
.size()))
850 ChildState cs
= (ChildState
) childStates
.get(index
);
851 view
= cs
.getChildView();
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
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
880 public int getViewIndex(int pos
, Position
.Bias bias
)
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
889 int numChildren
= childStates
.size();
892 for (int i
= 0; i
< numChildren
; ++i
)
894 View child
= ((ChildState
) childStates
.get(i
)).getChildView();
895 if (child
.getStartOffset() <= pos
&& child
.getEndOffset() > pos
)
906 * Returns the top inset.
908 * @return the top inset
910 public float getTopInset()
916 * Sets the top inset.
918 * @param top the top inset
920 public void setTopInset(float top
)
926 * Returns the bottom inset.
928 * @return the bottom inset
930 public float getBottomInset()
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()
956 * Sets the left inset.
958 * @param left the left inset
960 public void setLeftInset(float left
)
966 * Returns the right inset.
968 * @return the right inset
970 public float getRightInset()
976 * Sets the right inset.
978 * @param right the right inset
980 public void setRightInset(float 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();
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
1015 protected float getInsetSpan(int axis
)
1019 span
= leftInset
+ rightInset
;
1021 span
= topInset
+ bottomInset
;
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
1033 protected void setEstimatedMajorSpan(boolean estimated
)
1035 estimatedMajorSpan
= estimated
;
1039 * Determines whether the major span should be treated as estimated or as
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
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
)
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();
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
)
1111 if (majorAxis
== X_AXIS
)
1112 targetSpan
= height
- getTopInset() - getBottomInset();
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;
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);
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
)
1194 if (majorAxis
== axis
)
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
1208 retVal
= getLeftInset() + getRightInset() + 30;
1210 retVal
= getTopInset() + getBottomInset() + 30;
1216 * Maps a model location to view coordinates.
1218 * @param pos the model location
1219 * @param a the current allocation of this view
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
);
1233 View cv
= cs
.getChildView();
1234 Shape v
= cv
.modelToView(pos
, ca
, b
);
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
)
1254 synchronized (locator
)
1256 index
= locator
.getViewIndexAtPoint(x
, y
, a
);
1257 ca
= locator
.getChildAllocation(index
, a
);
1260 ChildState cs
= getChildState(index
);
1263 View v
= cs
.getChildView();
1264 pos
= v
.viewToModel(x
, y
, ca
, b
);
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
);
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
)
1297 if (axis
== majorAxis
)
1298 max
= getPreferredSpan(axis
);
1300 max
= Short
.MAX_VALUE
;
1305 * Returns the minimum span along the specified axis.
1307 public float getMinimumSpan(int axis
)
1310 if (axis
== majorAxis
)
1311 min
= getPreferredSpan(axis
);
1316 View child
= minReq
.getChildView();
1317 min
= child
.getMinimumSpan(axis
);
1321 // No layout information yet. Return insets + 5 as some kind of
1324 min
= getLeftInset() + getRightInset() + 5;
1326 min
= getTopInset() + getBottomInset() + 5;
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
,
1346 getParent().preferenceChanged(this, width
, height
);
1349 if (changing
!= null)
1351 View cv
= changing
.getChildView();
1354 changing
.preferenceChanged(width
, height
);
1358 int index
= getViewIndexAtPosition(view
.getStartOffset(),
1359 Position
.Bias
.Forward
);
1360 ChildState cs
= getChildState(index
);
1361 cs
.preferenceChanged(width
, height
);
1362 LayoutQueue q
= getLayoutQueue();
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
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
)
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
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();
1459 if (majorAxis
== X_AXIS
)
1461 horizontal
= majorChanged
;
1462 vertical
= minorChanged
;
1466 vertical
= majorChanged
;
1467 horizontal
= minorChanged
;
1470 p
.preferenceChanged(this, horizontal
, vertical
);
1471 majorChanged
= false;
1472 minorChanged
= false;
1474 Component c
= getContainer();