home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1998 November
/
Chip_1998-11_cd.bin
/
tema
/
Cafe
/
jfc.bin
/
ParagraphView.java
< prev
next >
Wrap
Text File
|
1998-02-26
|
32KB
|
864 lines
/*
* @(#)ParagraphView.java 1.40 98/02/01
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.text;
import java.util.Vector;
import java.util.Properties;
import java.awt.*;
import com.sun.java.swing.event.*;
/**
* View of a simple line-wrapping paragraph that supports
* multiple fonts, colors, components, icons, etc. It is
* basically a vertical box with a margin around it. The
* contents of the box are a bunch of rows which are special
* horizontal boxes. This view creates a collection of
* views that represent the child elements of the paragraph
* element. Each of these views are placed into a row
* directly if they will fit, otherwise the <code>breakView</code>
* method is called to try and carve the view into pieces
* that fit.
*
* @author Timothy Prinzing
* @author Scott Violet
* @version 1.40 02/01/98
* @see View
*/
public class ParagraphView extends BoxView implements TabExpander {
/**
* Constructs a ParagraphView for the given element.
*
* @param elem the element that this view is responsible for
*/
public ParagraphView(Element elem) {
super(elem, View.Y_AXIS);
AttributeSet attr = elem.getAttributes();
setParagraphInsets(attr);
justification = StyleConstants.getAlignment(attr);
lineSpacing = (int) StyleConstants.getLineSpacing(attr);
layoutSpan = Integer.MAX_VALUE;
}
/**
* Loads all of the children to initialize the view.
* This is called by the <code>setParent</code> method.
* This is reimplemented to not load any children directly
* (as they are created in the process of formatting).
* This does create views to represent the child elements,
* but they are placed into a pool that is used in the
* process of formatting.
*
* @param f the view factory
*/
protected void loadChildren(ViewFactory f) {
layoutPool = new Vector();
Element e = getElement();
int n = e.getElementCount();
for (int i = 0; i < n; i++) {
layoutPool.addElement(f.create(e.getElement(i)));
}
}
/**
* Fetches the child view that represents the given position in
* the model. This is implemented to walk through the children
* looking for a range that contains the given position. In this
* view the children do not have a one to one mapping with the
* child elements (ie. the children are actually rows that
* represent a portion of the element this view represents).
*
* @param pos the search position
* @param a the allocation to the box on entry, and the
* allocation of the view containing the position on exit
* @returns the view representing the given position, or
* null if there isn't one
*/
protected View getViewAtPosition(int pos, Rectangle a) {
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
int p0 = v.getStartOffset();
int p1 = v.getEndOffset();
if ((pos >= p0) && (pos < p1)) {
// it's in this view.
childAllocation(i, a);
return v;
}
}
return null;
}
/**
* Lays out the children. If the layout span has changed,
* the rows are rebuilt. The superclass functionality
* is called after checking and possibly rebuilding the
* rows. If the height has changed, the
* <code> preferenceChanged</code> method is called
* on the parent since the vertical preference is
* rigid.
*
* @param width the width to lay out against. This is
* the width inside of the inset area.
* @param height the height to lay out against (not used
* by paragraph, but used by the superclass). This
* is the height inside of the inset area.
*/
protected void layout(int width, int height) {
if (layoutSpan != width) {
int oldHeight = (int) getPreferredSpan(View.Y_AXIS);
rebuildRows(width);
int newHeight = (int) getPreferredSpan(View.Y_AXIS);
if (oldHeight != newHeight) {
View p = getParent();
p.preferenceChanged(this, false, true);
}
}
// do normal box layout
super.layout(width, height);
}
/**
* Does a a full layout on this View. This causes all of
* the rows (child views) to be rebuilt to match the given
* span of the given allocation.
*
* @param span the length to layout against.
*/
void rebuildRows(int span) {
layoutSpan = span;
int p0 = getStartOffset();
int p1 = getEndOffset();
removeAll();
while(p0 < p1) {
int old = p0;
// PENDING(prinz) The old rows should be reused and
// new ones created only if needed... and discarded
// only if not needed.
Row row = new Row(getElement());
append(row);
// layout the row to the current span
layoutRow(row, p0);
p0 = row.getEndOffset();
if (p0 <= old) {
throw new StateInvariantError("infinite loop in formatting");
}
}
}
/**
* Creates a row of views that will fit within the
* current layout span.
*
* @param row the row to fill in with views. This is assumed
* to be empty on entry.
* @param pos The current position in the children of
* this views element from which to start.
*/
void layoutRow(Row row, int pos) {
int x = tabBase;
int spanLeft = layoutSpan;
int end = getEndOffset();
while (pos < end && spanLeft > 0) {
View v = createView(pos);
int chunkSpan;
if (v instanceof TabableView) {
chunkSpan = (int) ((TabableView)v).getTabbedSpan(x, this);
} else {
chunkSpan = (int) v.getPreferredSpan(View.X_AXIS);
}
spanLeft -= chunkSpan;
x += chunkSpan;
row.append(v);
pos = v.getEndOffset();
}
if (spanLeft < 0) {
// This row is too long and needs to be adjusted.
adjustRow(row, layoutSpan);
} else if (row.getViewCount() == 0) {
// Impossible spec... put in whatever is left.
View v = createView(pos);
row.append(v);
}
}
/**
* Adjust the given row if possible to fit within the
* layout span. By default this will try to find the
* highest break weight possible nearest the end of
* the row. If a forced break is encountered, the
* break will be positioned there.
*
* @param r the row to adjust to the current layout
* span.
* @param desiredSpan the current layout span
*/
protected void adjustRow(Row r, int desiredSpan) {
int n = r.getViewCount();
int span = 0;
int bestWeight = BadBreakWeight;
int bestSpan = 0;
int bestIndex = -1;
int bestOffset = 0;
View v;
for (int i = 0; i < n; i++) {
v = r.getView(i);
int spanLeft = desiredSpan - span;
int w = v.getBreakWeight(X_AXIS, span, spanLeft);
if (w >= bestWeight) {
bestWeight = w;
bestIndex = i;
bestSpan = span;
if (w >= ForcedBreakWeight) {
// it's a forced break, so there is
// no point in searching further.
break;
}
}
span += v.getPreferredSpan(X_AXIS);
}
if (bestIndex < 0) {
// there is nothing that can be broken, leave
// it in it's current state.
return;
}
// Break the best candidate view, and patch up the row.
int spanLeft = desiredSpan - bestSpan;
v = r.getView(bestIndex);
v = v.breakView(X_AXIS, v.getStartOffset(), bestSpan, spanLeft);
View[] va = new View[1];
va[0] = v;
r.replace(bestIndex, n - bestIndex, va);
}
/**
* Creates a view that can be used to represent the
* current chunk. This is either the entire view from
* the pool of views being formatted, or it's the
* remaining portion of the view
*/
View createView(int pos) {
int childIndex = getElement().getElementIndex(pos);
View v = (View) layoutPool.elementAt(childIndex);
if (pos == v.getStartOffset()) {
// return the entire view
return v;
}
// return the remaining portion
v = v.createFragment(pos, v.getEndOffset());
return v;
}
// --- TabExpander methods ------------------------------------------
/**
* Returns the next tab stop position given a reference position.
* This view implements the tab coordinate system, and calls
* <code>getTabbedSpan</code> on the logical children in the process
* of layout to determine the desired span of the children. The
* logical children can delegate their tab expansion upward to
* the paragraph which knows how to expand tabs.
* <code>LabelView</code> is an example of a view that delegates
* it's tab expansion needs upward to the paragraph.
* <p>
* This is implemented to try and locate a <code>TabSet</code>
* in the paragraph element's attribute set. If one can be
* found, it's settings will be used, otherwise a default expansion
* will be provided. The base location for for tab expansion
* is the left inset from the paragraphs most recent allocation
* (which is what the layout of the children is based upon).
*
* @param x the position
* @param tabOffset the position within the text stream
* that the tab occurred at.
* @return the trailing end of the tab expansion
* @see TabSet
* @see TabStop
* @see LabelView
*/
public float nextTabStop(float x, int tabOffset) {
x -= tabBase;
TabSet tabs = StyleConstants.getTabSet
(getElement().getAttributes());
if(tabs == null) {
// a tab every 72 pixels.
return (float)(tabBase + (((int)x / 72 + 1) * 72));
}
TabStop tab = tabs.getTabAfter(x + .01f);
if(tab == null) {
// no tab, do a default of 5 pixels.
// Should this cause a wrapping of the line?
return tabBase + x + 5.0f;
}
int alignment = tab.getAlignment();
int offset;
switch(alignment) {
default:
case TabStop.ALIGN_LEFT:
// Simple case, left tab.
return tabBase + tab.getPosition();
case TabStop.ALIGN_BAR:
// PENDING: what does this mean?
return tabBase + tab.getPosition();
case TabStop.ALIGN_RIGHT:
case TabStop.ALIGN_CENTER:
offset = findOffsetToCharactersInString(tabChars,
tabOffset + 1);
break;
case TabStop.ALIGN_DECIMAL:
offset = findOffsetToCharactersInString(tabDecimalChars,
tabOffset + 1);
break;
}
if (offset == -1) {
offset = getEndOffset();
}
float charsSize = getPartialSize(tabOffset + 1, offset);
switch(alignment) {
case TabStop.ALIGN_RIGHT:
case TabStop.ALIGN_DECIMAL:
// right and decimal are treated the same way, the new
// position will be the location of the tab less the
// partialSize.
return tabBase + Math.max(x, tab.getPosition() - charsSize);
case TabStop.ALIGN_CENTER:
// Similar to right, but half the partialSize.
return tabBase + Math.max(x, tab.getPosition() - charsSize / 2.0f);
}
// will never get here!
return x;
}
/**
* Returns the size used by the views between <code>startOffset</code>
* and <code>endOffset</code>. This uses getPartialView to calculate the
* size if the child view implements the TabableView interface. If a
* size is needed and a View does not implement the TabableView
* interface, the preferredSpan will be used.
*/
protected float getPartialSize(int startOffset, int endOffset) {
float size = 0.0f;
int viewIndex;
int numViews = getViewCount();
View view;
int viewEnd;
int tempEnd;
// Have to search layoutPool!
// PENDING: when ParagraphView supports breaking location
// into layoutPool will have to change!
viewIndex = getElement().getElementIndex(startOffset);
numViews = layoutPool.size();
while(startOffset < endOffset && viewIndex < numViews) {
view = (View) layoutPool.elementAt(viewIndex++);
viewEnd = view.getEndOffset();
tempEnd = Math.min(endOffset, viewEnd);
if(view instanceof TabableView)
size += ((TabableView)view).getPartialSpan(startOffset, tempEnd);
else if(startOffset == view.getStartOffset() &&
tempEnd == view.getEndOffset())
size += view.getPreferredSpan(View.X_AXIS);
else
// PENDING: should we handle this better?
return 0.0f;
startOffset = viewEnd;
}
return size;
}
/**
* Finds the next character in the document with a character in
* <code>string</code>, starting at offset <code>start</code>. If
* there are no characters found, -1 will be returned.
*/
protected int findOffsetToCharactersInString(char[] string,
int start) {
int stringLength = string.length;
int end = getEndOffset();
Segment seg = new Segment();
try {
getDocument().getText(start, end - start, seg);
} catch (BadLocationException ble) {
return -1;
}
for(int counter = seg.offset, maxCounter = seg.offset + seg.count;
counter < maxCounter; counter++) {
char currentChar = seg.array[counter];
for(int subCounter = 0; subCounter < stringLength;
subCounter++) {
if(currentChar == string[subCounter])
return counter - seg.offset + start;
}
}
// No match.
return -1;
}
// ---- View methods ----------------------------------------------------
/**
* Renders using the given rendering surface and area on that
* surface. This is implemented to delgate to the superclass
* after stashing the base coordinate for tab calculations.
*
* @param g the rendering surface to use
* @param a the allocated region to render into
* @see View#paint
*/
public void paint(Graphics g, Shape a) {
Rectangle alloc = a.getBounds();
tabBase = alloc.x + getLeftInset();
super.paint(g, a);
}
/**
* Determines the preferred span for this view along an
* axis. For the paragraph it's whatever it was formatted
* to along the x axis and whatever the box calculation is
* for the y axis.
*
* @param axis may be either X_AXIS or Y_AXIS
* @returns the span the view would like to be rendered into.
* Typically the view is told to render into the span
* that is returned, although there is no guarantee.
* The parent may choose to resize or break the view.
* @exception IllegalArgumentException for an invalid axis
*/
public float getPreferredSpan(int axis) {
switch (axis) {
case View.X_AXIS:
int cheapSpan = (int) Math.min((long) layoutSpan +
(long) getLeftInset() +
(long) getRightInset(),
Integer.MAX_VALUE);
return (layoutSpan != Integer.MAX_VALUE) ?
cheapSpan : super.getPreferredSpan(axis);
case View.Y_AXIS:
return super.getPreferredSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/**
* Determines the desired alignment for this view along an
* axis. This is implemented to give the alignment to the
* center of the first row along the y axis, and the default
* along the x axis.
*
* @param axis may be either X_AXIS or Y_AXIS
* @returns the desired alignment. This should be a value
* between 0.0 and 1.0 where 0 indicates alignment at the
* origin and 1.0 indicates alignment to the full span
* away from the origin. An alignment of 0.5 would be the
* center of the view.
*/
public float getAlignment(int axis) {
switch (axis) {
case View.Y_AXIS:
int paragraphSpan = (int) getPreferredSpan(View.Y_AXIS);
View v = getView(0);
int rowSpan = (int) v.getPreferredSpan(View.Y_AXIS);
float a = (paragraphSpan != 0) ? ((float)(rowSpan / 2)) / paragraphSpan : 0;
return a;
default:
return super.getAlignment(axis);
}
}
/**
* Gets the resize weight. A value of 0 or less is not resizable.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the weight
* @exception IllegalArgumentException for an invalid axis
*/
public int getResizeWeight(int axis) {
switch (axis) {
case View.X_AXIS:
return 1;
case View.Y_AXIS:
return 0;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/**
* Breaks this view on the given axis at the given length.<p>
* ParagraphView instances are breakable along the Y_AXIS only, and only if
* <code>len</code> is after the first line.
*
* @param axis may be either X_AXIS or Y_AXIS
* @param len specifies where a potential break is desired
* along the given axis
* @param a the current allocation of the view
* @return the fragment of the view that represents the
* given span, if the view can be broken. If the view
* doesn't support breaking behavior, the view itself is
* returned.
* @see View#breakView
*/
public View breakView(int axis, float len, Shape a) {
if(axis == View.Y_AXIS) {
if(a != null) {
Rectangle alloc = a.getBounds();
setSize(alloc.width, alloc.height);
}
// Determine what row to break on.
// PENDING(prinz) add break support
return this;
}
return this;
}
/**
* ParagraphView instances are breakable along the Y_AXIS only, and
* only if <code>len</code> is after the first row. If the length
* is less than one row, a value of BadBreakWeight is returned.
*
* @param axis may be either X_AXIS or Y_AXIS
* @param len specifies where a potential break is desired
* @return a value indicating the attractiveness of breaking here
* @see View#getBreakWeight
*/
public int getBreakWeight(int axis, float len) {
if(axis == View.Y_AXIS) {
// PENDING(prinz) make this return a reasonable value
// when paragraph breaking support is re-implemented.
// If less than one row, bad weight value should be
// returned.
//return GoodBreakWeight;
return BadBreakWeight;
}
return BadBreakWeight;
}
/**
* Gives notification that something was inserted into the document
* in a location that this view is responsible for.
*
* @param changes the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#insertUpdate
*/
public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
// update the pool of logical children
Element elem = getElement();
DocumentEvent.ElementChange ec = changes.getChange(elem);
if (ec != null) {
// the structure of this element changed.
updateLogicalChildren(ec, f);
}
// find and forward if there is anything there to
// forward to. If children were removed then there was
// a replacement of the removal range and there is no
// need to forward.
if (ec == null || (ec.getChildrenRemoved().length == 0)) {
int pos = changes.getOffset();
int index = elem.getElementIndex(pos);
View v = (View) layoutPool.elementAt(index);
v.insertUpdate(changes, null, f);
}
// force layout, should do something more intelligent about
// incurring damage and triggering a new layout. This is just
// about as brute force as it can get.
layoutSpan = Integer.MAX_VALUE;
preferenceChanged(null, false, true);
Rectangle alloc = getInsideAllocation(a);
if (alloc != null) {
layout((int) alloc.width, (int) alloc.height);
Component host = getContainer();
host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
}
}
/**
* Update the logical children to reflect changes made
* to the element this view is responsible. This updates
* the pool of views used for layout (ie. the views
* representing the child elements of the element this
* view is responsible for). This is called by the
* <code>insertUpdate, removeUpdate, and changeUpdate</code>
* methods.
*/
void updateLogicalChildren(DocumentEvent.ElementChange ec, ViewFactory f) {
int index = ec.getIndex();
Element[] removedElems = ec.getChildrenRemoved();
for (int i = 0; i < removedElems.length; i++) {
View v = (View) layoutPool.elementAt(index);
v.setParent(null);
layoutPool.removeElementAt(index);
}
Element[] addedElems = ec.getChildrenAdded();
for (int i = 0; i < addedElems.length; i++) {
layoutPool.insertElementAt(f.create(addedElems[i]),
index + i);
}
}
/**
* Gives notification that something was removed from the document
* in a location that this view is responsible for.
*
* @param changes the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#removeUpdate
*/
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
// update the pool of logical children
Element elem = getElement();
DocumentEvent.ElementChange ec = changes.getChange(elem);
if (ec != null) {
// the structure of this element changed.
updateLogicalChildren(ec, f);
}
// find and forward if there is anything there to
// forward to. If children were added then there was
// a replacement of the removal range and there is no
// need to forward.
if (ec == null || (ec.getChildrenAdded().length == 0)) {
int pos = changes.getOffset();
int index = elem.getElementIndex(pos);
View v = (View) layoutPool.elementAt(index);
v.removeUpdate(changes, null, f);
}
// force layout, should do something more intelligent about
// incurring damage and triggering a new layout.
layoutSpan = Integer.MAX_VALUE;
preferenceChanged(null, false, true);
if (a != null) {
Rectangle alloc = getInsideAllocation(a);
layout((int) alloc.width, (int) alloc.height);
Component host = getContainer();
host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
}
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for.
*
* @param changes the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#changedUpdate
*/
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
// update any property settings stored
AttributeSet attr = getElement().getAttributes();
setParagraphInsets(attr);
justification = StyleConstants.getAlignment(attr);
lineSpacing = (int) StyleConstants.getLineSpacing(attr);
// update the pool of logical children
Element elem = getElement();
DocumentEvent.ElementChange ec = changes.getChange(elem);
if (ec != null) {
// the structure of this element changed.
updateLogicalChildren(ec, f);
}
// forward to the logical children
int p0 = changes.getOffset();
int p1 = p0 + changes.getLength();
int index0 = elem.getElementIndex(p0);
int index1 = elem.getElementIndex(p1 - 1);
for (int i = index0; i <= index1; i++) {
View v = (View) layoutPool.elementAt(i);
v.changedUpdate(changes, null, f);
}
// force layout, should do something more intelligent about
// incurring damage and triggering a new layout.
layoutSpan = Integer.MAX_VALUE;
preferenceChanged(null, false, true);
if (a != null) {
Rectangle alloc = getInsideAllocation(a);
layout((int) alloc.width, (int) alloc.height);
Component host = getContainer();
host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
}
}
// --- variables -----------------------------------------------
private int justification;
private int lineSpacing;
/**
* Used by the TabExpander functionality to determine
* where to base the tab calculations. This is basically
* the location of the left inset.
*/
private int tabBase;
/**
* Used by the layout process. The span holds the
* length that has been formatted to.
*/
private int layoutSpan;
/**
* These are the views that represent the child elements
* of the element this view represents. These are not
* directly children of this view. These are either
* placed into the rows directly or used for the purpose
* of breaking into smaller chunks.
*/
private Vector layoutPool;
/** Used for searching for a tab. */
static char[] tabChars;
/** Used for searching for a tab or decimal character. */
static char[] tabDecimalChars;
static {
tabChars = new char[1];
tabChars[0] = '\t';
tabDecimalChars = new char[2];
tabDecimalChars[0] = '\t';
tabDecimalChars[1] = '.';
}
/**
* Internally created view that has the purpose of holding
* the views that represent the children of the paragraph
* that have been arranged in rows.
*/
class Row extends BoxView {
Row(Element elem) {
super(elem, View.X_AXIS);
}
/**
* This is reimplemented to do nothing since the
* paragraph fills in the row with its needed
* children.
*/
protected void loadChildren(ViewFactory f) {
}
public float getAlignment(int axis) {
if (axis == View.X_AXIS) {
switch (justification) {
case StyleConstants.ALIGN_LEFT:
return 0;
case StyleConstants.ALIGN_RIGHT:
return 1;
case StyleConstants.ALIGN_CENTER:
case StyleConstants.ALIGN_JUSTIFIED:
return 0.5f;
}
}
return super.getAlignment(axis);
}
/**
* Provides a mapping from the document model coordinate space
* to the coordinate space of the view mapped to it. This is
* implemented to let the superclass find the position along
* the major axis and the allocation of the row is used
* along the minor axis, so that even though the children
* are different heights they all get the same caret height.
*
* @param pos the position to convert
* @param a the allocated region to render into
* @return the bounding box of the given position
* @exception BadLocationException if the given position does not represent a
* valid location in the associated document
* @see View#modelToView
*/
public Shape modelToView(int pos, Shape a) throws BadLocationException {
Rectangle r = a.getBounds();
int height = r.height;
int y = r.y;
Shape loc = super.modelToView(pos, a);
r = loc.getBounds();
r.height = height;
r.y = y;
return r;
}
/**
* Range represented by a row in the paragraph is only
* a subset of the total range of the paragraph element.
* @see View#getRange
*/
public int getStartOffset() {
int n = getViewCount();
if (n > 0) {
View v = getView(0);
return v.getStartOffset();
}
return -1;
}
public int getEndOffset() {
int n = getViewCount();
if (n > 0) {
View v = getView(n-1);
return v.getEndOffset();
}
return -1;
}
/**
* Fetches the child view that represents the given position in
* the model. This is implemented to walk through the children
* looking for a range that contains the given position.
* @param pos The search position
* @param a The allocation to the box on entry, and the
* allocation of the view containing the position on exit.
* @returns The view representing the given position, or
* null if there isn't one.
*/
protected View getViewAtPosition(int pos, Rectangle a) {
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
int p0 = v.getStartOffset();
int p1 = v.getEndOffset();
if ((pos >= p0) && (pos < p1)) {
// it's in this view.
this.childAllocation(i, a);
return v;
}
}
return null;
}
}
}