- /*
 - * @(#)Utilities.java 1.40 03/01/23
 - *
 - * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 - */
 - package javax.swing.text;
 - import java.lang.reflect.Method;
 - import java.awt.Rectangle;
 - import java.awt.Graphics;
 - import java.awt.FontMetrics;
 - import java.awt.Shape;
 - import java.awt.Toolkit;
 - import java.awt.Graphics2D;
 - import java.awt.font.FontRenderContext;
 - import java.awt.font.TextLayout;
 - import java.awt.font.TextAttribute;
 - import java.text.*;
 - import javax.swing.SwingConstants;
 - /**
 - * A collection of methods to deal with various text
 - * related activities.
 - *
 - * @author Timothy Prinzing
 - * @version 1.40 01/23/03
 - */
 - public class Utilities {
 - /**
 - * Draws the given text, expanding any tabs that are contained
 - * using the given tab expansion technique. This particular
 - * implementation renders in a 1.1 style coordinate system
 - * where ints are used and 72dpi is assumed.
 - *
 - * @param s the source of the text
 - * @param x the X origin >= 0
 - * @param y the Y origin >= 0
 - * @param g the graphics context
 - * @param e how to expand the tabs. If this value is null,
 - * tabs will be expanded as a space character.
 - * @param startOffset starting offset of the text in the document >= 0
 - * @return the X location at the end of the rendered text
 - */
 - public static final int drawTabbedText(Segment s, int x, int y, Graphics g,
 - TabExpander e, int startOffset) {
 - FontMetrics metrics = g.getFontMetrics();
 - int nextX = x;
 - char[] txt = s.array;
 - int txtOffset = s.offset;
 - int flushLen = 0;
 - int flushIndex = s.offset;
 - int n = s.offset + s.count;
 - for (int i = txtOffset; i < n; i++) {
 - if (txt[i] == '\t') {
 - if (flushLen > 0) {
 - g.drawChars(txt, flushIndex, flushLen, x, y);
 - flushLen = 0;
 - }
 - flushIndex = i + 1;
 - if (e != null) {
 - nextX = (int) e.nextTabStop((float) nextX, startOffset + i - txtOffset);
 - } else {
 - nextX += metrics.charWidth(' ');
 - }
 - x = nextX;
 - } else if ((txt[i] == '\n') || (txt[i] == '\r')) {
 - if (flushLen > 0) {
 - g.drawChars(txt, flushIndex, flushLen, x, y);
 - flushLen = 0;
 - }
 - flushIndex = i + 1;
 - x = nextX;
 - } else {
 - flushLen += 1;
 - nextX += metrics.charWidth(txt[i]);
 - }
 - }
 - if (flushLen > 0) {
 - g.drawChars(txt, flushIndex, flushLen, x, y);
 - }
 - return nextX;
 - }
 - /**
 - * Determines the width of the given segment of text taking tabs
 - * into consideration. This is implemented in a 1.1 style coordinate
 - * system where ints are used and 72dpi is assumed.
 - *
 - * @param s the source of the text
 - * @param metrics the font metrics to use for the calculation
 - * @param x the X origin >= 0
 - * @param e how to expand the tabs. If this value is null,
 - * tabs will be expanded as a space character.
 - * @param startOffset starting offset of the text in the document >= 0
 - * @return the width of the text
 - */
 - public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x,
 - TabExpander e, int startOffset) {
 - int nextX = x;
 - char[] txt = s.array;
 - int txtOffset = s.offset;
 - int n = s.offset + s.count;
 - for (int i = txtOffset; i < n; i++) {
 - if (txt[i] == '\t') {
 - if (e != null) {
 - nextX = (int) e.nextTabStop((float) nextX,
 - startOffset + i - txtOffset);
 - } else {
 - nextX += metrics.charWidth(' ');
 - }
 - } else if(txt[i] != '\n') {
 - nextX += metrics.charWidth(txt[i]);
 - }
 - // Ignore newlines, they take up space and we shouldn't be
 - // counting them.
 - }
 - return nextX - x;
 - }
 - /**
 - * Determines the relative offset into the given text that
 - * best represents the given span in the view coordinate
 - * system. This is implemented in a 1.1 style coordinate
 - * system where ints are used and 72dpi is assumed.
 - *
 - * @param s the source of the text
 - * @param metrics the font metrics to use for the calculation
 - * @param x0 the starting view location representing the start
 - * of the given text >= 0.
 - * @param x the target view location to translate to an
 - * offset into the text >= 0.
 - * @param e how to expand the tabs. If this value is null,
 - * tabs will be expanded as a space character.
 - * @param startOffset starting offset of the text in the document >= 0
 - * @return the offset into the text >= 0
 - */
 - public static final int getTabbedTextOffset(Segment s, FontMetrics metrics,
 - int x0, int x, TabExpander e,
 - int startOffset) {
 - return getTabbedTextOffset(s, metrics, x0, x, e, startOffset, true);
 - }
 - public static final int getTabbedTextOffset(Segment s,
 - FontMetrics metrics,
 - int x0, int x, TabExpander e,
 - int startOffset,
 - boolean round) {
 - if (x0 >= x) {
 - // x before x0, return.
 - return 0;
 - }
 - int currX = x0;
 - int nextX = currX;
 - // s may be a shared segment, so it is copied prior to calling
 - // the tab expander
 - char[] txt = s.array;
 - int txtOffset = s.offset;
 - int txtCount = s.count;
 - int n = s.offset + s.count;
 - for (int i = s.offset; i < n; i++) {
 - if (txt[i] == '\t') {
 - if (e != null) {
 - nextX = (int) e.nextTabStop((float) nextX,
 - startOffset + i - txtOffset);
 - } else {
 - nextX += metrics.charWidth(' ');
 - }
 - } else {
 - nextX += metrics.charWidth(txt[i]);
 - }
 - if ((x >= currX) && (x < nextX)) {
 - // found the hit position... return the appropriate side
 - if ((round == false) || ((x - currX) < (nextX - x))) {
 - return i - txtOffset;
 - } else {
 - return i + 1 - txtOffset;
 - }
 - }
 - currX = nextX;
 - }
 - // didn't find, return end offset
 - return txtCount;
 - }
 - /**
 - * Determine where to break the given text to fit
 - * within the the given span. This tries to find a
 - * whitespace boundary.
 - * @param s the source of the text
 - * @param metrics the font metrics to use for the calculation
 - * @param x0 the starting view location representing the start
 - * of the given text.
 - * @param x the target view location to translate to an
 - * offset into the text.
 - * @param e how to expand the tabs. If this value is null,
 - * tabs will be expanded as a space character.
 - * @param startOffset starting offset in the document of the text
 - * @return the offset into the given text
 - */
 - public static final int getBreakLocation(Segment s, FontMetrics metrics,
 - int x0, int x, TabExpander e,
 - int startOffset) {
 - char[] txt = s.array;
 - int txtOffset = s.offset;
 - int txtCount = s.count;
 - int index = Utilities.getTabbedTextOffset(s, metrics, x0, x,
 - e, startOffset, false);
 - for (int i = txtOffset + Math.min(index, txtCount - 1);
 - i >= txtOffset; i--) {
 - char ch = txt[i];
 - if (Character.isWhitespace(ch)) {
 - // found whitespace, break here
 - index = i - txtOffset + 1;
 - break;
 - }
 - }
 - return index;
 - }
 - /**
 - * Determines the starting row model position of the row that contains
 - * the specified model position. The component given must have a
 - * size to compute the result. If the component doesn't have a size
 - * a value of -1 will be returned.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @return the position >= 0 if the request can be computed, otherwise
 - * a value of -1 will be returned.
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getRowStart(JTextComponent c, int offs) throws BadLocationException {
 - Rectangle r = c.modelToView(offs);
 - if (r == null) {
 - return -1;
 - }
 - int lastOffs = offs;
 - int y = r.y;
 - while ((r != null) && (y == r.y)) {
 - offs = lastOffs;
 - lastOffs -= 1;
 - r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
 - }
 - return offs;
 - }
 - /**
 - * Determines the ending row model position of the row that contains
 - * the specified model position. The component given must have a
 - * size to compute the result. If the component doesn't have a size
 - * a value of -1 will be returned.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @return the position >= 0 if the request can be computed, otherwise
 - * a value of -1 will be returned.
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getRowEnd(JTextComponent c, int offs) throws BadLocationException {
 - Rectangle r = c.modelToView(offs);
 - if (r == null) {
 - return -1;
 - }
 - int n = c.getDocument().getLength();
 - int lastOffs = offs;
 - int y = r.y;
 - while ((r != null) && (y == r.y)) {
 - offs = lastOffs;
 - lastOffs += 1;
 - r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
 - }
 - return offs;
 - }
 - /**
 - * Determines the position in the model that is closest to the given
 - * view location in the row above. The component given must have a
 - * size to compute the result. If the component doesn't have a size
 - * a value of -1 will be returned.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @param x the X coordinate >= 0
 - * @return the position >= 0 if the request can be computed, otherwise
 - * a value of -1 will be returned.
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getPositionAbove(JTextComponent c, int offs, int x) throws BadLocationException {
 - int lastOffs = getRowStart(c, offs) - 1;
 - if (lastOffs < 0) {
 - return -1;
 - }
 - int bestSpan = Short.MAX_VALUE;
 - int y = 0;
 - Rectangle r = null;
 - if (lastOffs >= 0) {
 - r = c.modelToView(lastOffs);
 - y = r.y;
 - }
 - while ((r != null) && (y == r.y)) {
 - int span = Math.abs(r.x - x);
 - if (span < bestSpan) {
 - offs = lastOffs;
 - bestSpan = span;
 - }
 - lastOffs -= 1;
 - r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
 - }
 - return offs;
 - }
 - /**
 - * Determines the position in the model that is closest to the given
 - * view location in the row below. The component given must have a
 - * size to compute the result. If the component doesn't have a size
 - * a value of -1 will be returned.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @param x the X coordinate >= 0
 - * @return the position >= 0 if the request can be computed, otherwise
 - * a value of -1 will be returned.
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getPositionBelow(JTextComponent c, int offs, int x) throws BadLocationException {
 - int lastOffs = getRowEnd(c, offs) + 1;
 - if (lastOffs <= 0) {
 - return -1;
 - }
 - int bestSpan = Short.MAX_VALUE;
 - int n = c.getDocument().getLength();
 - int y = 0;
 - Rectangle r = null;
 - if (lastOffs <= n) {
 - r = c.modelToView(lastOffs);
 - y = r.y;
 - }
 - while ((r != null) && (y == r.y)) {
 - int span = Math.abs(x - r.x);
 - if (span < bestSpan) {
 - offs = lastOffs;
 - bestSpan = span;
 - }
 - lastOffs += 1;
 - r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
 - }
 - return offs;
 - }
 - /**
 - * Determines the start of a word for the given model location.
 - * Uses BreakIterator.getWordInstance() to actually get the words.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @return the location in the model of the word start >= 0
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getWordStart(JTextComponent c, int offs) throws BadLocationException {
 - Document doc = c.getDocument();
 - Element line = getParagraphElement(c, offs);
 - if (line == null) {
 - throw new BadLocationException("No word at " + offs, offs);
 - }
 - int lineStart = line.getStartOffset();
 - int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
 - String s = doc.getText(lineStart, lineEnd - lineStart);
 - if(s != null && s.length() > 0) {
 - BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
 - words.setText(s);
 - int wordPosition = offs - lineStart;
 - if(wordPosition >= words.last()) {
 - wordPosition = words.last() - 1;
 - }
 - words.following(wordPosition);
 - offs = lineStart + words.previous();
 - }
 - return offs;
 - }
 - /**
 - * Determines the end of a word for the given location.
 - * Uses BreakIterator.getWordInstance() to actually get the words.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @return the location in the model of the word end >= 0
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getWordEnd(JTextComponent c, int offs) throws BadLocationException {
 - Document doc = c.getDocument();
 - Element line = getParagraphElement(c, offs);
 - if (line == null) {
 - throw new BadLocationException("No word at " + offs, offs);
 - }
 - int lineStart = line.getStartOffset();
 - int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
 - String s = doc.getText(lineStart, lineEnd - lineStart);
 - if(s != null && s.length() > 0) {
 - BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
 - words.setText(s);
 - int wordPosition = offs - lineStart;
 - if(wordPosition >= words.last()) {
 - wordPosition = words.last() - 1;
 - }
 - offs = lineStart + words.following(wordPosition);
 - }
 - return offs;
 - }
 - /**
 - * Determines the start of the next word for the given location.
 - * Uses BreakIterator.getWordInstance() to actually get the words.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @return the location in the model of the word start >= 0
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getNextWord(JTextComponent c, int offs) throws BadLocationException {
 - int nextWord;
 - Element line = getParagraphElement(c, offs);
 - for (nextWord = getNextWordInParagraph(c, line, offs, false);
 - nextWord == BreakIterator.DONE;
 - nextWord = getNextWordInParagraph(c, line, offs, true)) {
 - // didn't find in this line, try the next line
 - offs = line.getEndOffset();
 - line = getParagraphElement(c, offs);
 - }
 - return nextWord;
 - }
 - /**
 - * Finds the next word in the given elements text. The first
 - * parameter allows searching multiple paragraphs where even
 - * the first offset is desired.
 - * Returns the offset of the next word, or BreakIterator.DONE
 - * if there are no more words in the element.
 - */
 - static int getNextWordInParagraph(JTextComponent c, Element line, int offs, boolean first) throws BadLocationException {
 - if (line == null) {
 - throw new BadLocationException("No more words", offs);
 - }
 - Document doc = line.getDocument();
 - int lineStart = line.getStartOffset();
 - int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
 - if ((offs >= lineEnd) || (offs < lineStart)) {
 - throw new BadLocationException("No more words", offs);
 - }
 - String s = doc.getText(lineStart, lineEnd - lineStart);
 - BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
 - words.setText(s);
 - if ((first && (words.first() == (offs - lineStart))) &&
 - (! Character.isWhitespace(s.charAt(words.first())))) {
 - return offs;
 - }
 - int wordPosition = words.following(offs - lineStart);
 - if ((wordPosition == BreakIterator.DONE) ||
 - (wordPosition >= s.length())) {
 - // there are no more words on this line.
 - return BreakIterator.DONE;
 - }
 - // if we haven't shot past the end... check to
 - // see if the current boundary represents whitespace.
 - // if so, we need to try again
 - char ch = s.charAt(wordPosition);
 - if (! Character.isWhitespace(ch)) {
 - return lineStart + wordPosition;
 - }
 - // it was whitespace, try again. The assumption
 - // is that it must be a word start if the last
 - // one had whitespace following it.
 - wordPosition = words.next();
 - if (wordPosition != BreakIterator.DONE) {
 - offs = lineStart + wordPosition;
 - if (offs != lineEnd) {
 - return offs;
 - }
 - }
 - return BreakIterator.DONE;
 - }
 - /**
 - * Determine the start of the prev word for the given location.
 - * Uses BreakIterator.getWordInstance() to actually get the words.
 - *
 - * @param c the editor
 - * @param offs the offset in the document >= 0
 - * @return the location in the model of the word start >= 0
 - * @exception BadLocationException if the offset is out of range
 - */
 - public static final int getPreviousWord(JTextComponent c, int offs) throws BadLocationException {
 - int prevWord;
 - Element line = getParagraphElement(c, offs);
 - for (prevWord = getPrevWordInParagraph(c, line, offs);
 - prevWord == BreakIterator.DONE;
 - prevWord = getPrevWordInParagraph(c, line, offs)) {
 - // didn't find in this line, try the prev line
 - offs = line.getStartOffset() - 1;
 - line = getParagraphElement(c, offs);
 - }
 - return prevWord;
 - }
 - /**
 - * Finds the previous word in the given elements text. The first
 - * parameter allows searching multiple paragraphs where even
 - * the first offset is desired.
 - * Returns the offset of the next word, or BreakIterator.DONE
 - * if there are no more words in the element.
 - */
 - static int getPrevWordInParagraph(JTextComponent c, Element line, int offs) throws BadLocationException {
 - if (line == null) {
 - throw new BadLocationException("No more words", offs);
 - }
 - Document doc = line.getDocument();
 - int lineStart = line.getStartOffset();
 - int lineEnd = line.getEndOffset();
 - if ((offs > lineEnd) || (offs < lineStart)) {
 - throw new BadLocationException("No more words", offs);
 - }
 - String s = doc.getText(lineStart, lineEnd - lineStart);
 - BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
 - words.setText(s);
 - if (words.following(offs - lineStart) == BreakIterator.DONE) {
 - words.last();
 - }
 - int wordPosition = words.previous();
 - if (wordPosition == (offs - lineStart)) {
 - wordPosition = words.previous();
 - }
 - if (wordPosition == BreakIterator.DONE) {
 - // there are no more words on this line.
 - return BreakIterator.DONE;
 - }
 - // if we haven't shot past the end... check to
 - // see if the current boundary represents whitespace.
 - // if so, we need to try again
 - char ch = s.charAt(wordPosition);
 - if (! Character.isWhitespace(ch)) {
 - return lineStart + wordPosition;
 - }
 - // it was whitespace, try again. The assumption
 - // is that it must be a word start if the last
 - // one had whitespace following it.
 - wordPosition = words.previous();
 - if (wordPosition != BreakIterator.DONE) {
 - return lineStart + wordPosition;
 - }
 - return BreakIterator.DONE;
 - }
 - /**
 - * Determines the element to use for a paragraph/line.
 - *
 - * @param c the editor
 - * @param offs the starting offset in the document >= 0
 - * @return the element
 - */
 - public static final Element getParagraphElement(JTextComponent c, int offs) {
 - Document doc = c.getDocument();
 - if (doc instanceof StyledDocument) {
 - return ((StyledDocument)doc).getParagraphElement(offs);
 - }
 - Element map = doc.getDefaultRootElement();
 - int index = map.getElementIndex(offs);
 - Element paragraph = map.getElement(index);
 - if ((offs >= paragraph.getStartOffset()) && (offs < paragraph.getEndOffset())) {
 - return paragraph;
 - }
 - return null;
 - }
 - static boolean isComposedTextElement(Document doc, int offset) {
 - Element elem = doc.getDefaultRootElement();
 - while (!elem.isLeaf()) {
 - elem = elem.getElement(elem.getElementIndex(offset));
 - }
 - return isComposedTextElement(elem);
 - }
 - static boolean isComposedTextElement(Element elem) {
 - AttributeSet as = elem.getAttributes();
 - return isComposedTextAttributeDefined(as);
 - }
 - static boolean isComposedTextAttributeDefined(AttributeSet as) {
 - return ((as != null) &&
 - (as.isDefined(StyleConstants.ComposedTextAttribute)));
 - }
 - /**
 - * Draws the given composed text passed from an input method.
 - *
 - * @param attr the attributes containing the composed text
 - * @param g the graphics context
 - * @param x the X origin
 - * @param y the Y origin
 - * @param p0 starting offset in the composed text to be rendered
 - * @param p1 ending offset in the composed text to be rendered
 - * @return the new insertion position
 - */
 - static int drawComposedText(AttributeSet attr, Graphics g, int x, int y,
 - int p0, int p1) throws BadLocationException {
 - Graphics2D g2d = (Graphics2D)g;
 - AttributedString as = (AttributedString)attr.getAttribute(
 - StyleConstants.ComposedTextAttribute);
 - as.addAttribute(TextAttribute.FONT, g.getFont());
 - if (p0 >= p1)
 - return x;
 - AttributedCharacterIterator aci = as.getIterator(null, p0, p1);
 - // Create text layout
 - TextLayout layout = new TextLayout(aci, g2d.getFontRenderContext());
 - // draw
 - layout.draw(g2d, x, y);
 - return x + (int)layout.getAdvance();
 - }
 - /**
 - * Paints the composed text in a GlyphView
 - */
 - static void paintComposedText(Graphics g, Rectangle alloc, GlyphView v) {
 - if (g instanceof Graphics2D) {
 - Graphics2D g2d = (Graphics2D) g;
 - int p0 = v.getStartOffset();
 - int p1 = v.getEndOffset();
 - AttributeSet attrSet = v.getElement().getAttributes();
 - AttributedString as =
 - (AttributedString)attrSet.getAttribute(StyleConstants.ComposedTextAttribute);
 - int start = v.getElement().getStartOffset();
 - int y = alloc.y + (int) v.getGlyphPainter().getAscent(v);
 - int x = alloc.x;
 - //Add text attributes
 - as.addAttribute(TextAttribute.FONT, v.getFont());
 - as.addAttribute(TextAttribute.FOREGROUND, v.getForeground());
 - if (StyleConstants.isBold(v.getAttributes())) {
 - as.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
 - }
 - if (StyleConstants.isItalic(v.getAttributes())) {
 - as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
 - }
 - if (v.isUnderline()) {
 - as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
 - }
 - if (v.isStrikeThrough()) {
 - as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
 - }
 - if (v.isSuperscript()) {
 - as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
 - }
 - if (v.isSubscript()) {
 - as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
 - }
 - // draw
 - AttributedCharacterIterator aci = as.getIterator(null, p0 - start, p1 - start);
 - TextLayout layout = new TextLayout(aci, g2d.getFontRenderContext());
 - layout.draw(g2d, x, y);
 - }
 - }
 - /*
 - * Convenience function for determining ComponentOrientation. Helps us
 - * avoid having Munge directives throughout the code.
 - */
 - static boolean isLeftToRight( java.awt.Component c ) {
 - return c.getComponentOrientation().isLeftToRight();
 - }
 - /**
 - * Provides a way to determine the next visually represented model
 - * location that one might place a caret. Some views may not be visible,
 - * they might not be in the same order found in the model, or they just
 - * might not allow access to some of the locations in the model.
 - * <p>
 - * This implementation assumes the views are layed out in a logical
 - * manner. That is, that the view at index x + 1 is visually after
 - * the View at index x, and that the View at index x - 1 is visually
 - * before the View at x. There is support for reversing this behavior
 - * only if the passed in <code>View</code> is an instance of
 - * <code>CompositeView</code>. The <code>CompositeView</code>
 - * must then override the <code>flipEastAndWestAtEnds</code> method.
 - *
 - * @param v View to query
 - * @param pos the position to convert >= 0
 - * @param a the allocated region to render into
 - * @param direction the direction from the current position that can
 - * be thought of as the arrow keys typically found on a keyboard;
 - * this may be one of the following:
 - * <ul>
 - * <li><code>SwingConstants.WEST</code>
 - * <li><code>SwingConstants.EAST</code>
 - * <li><code>SwingConstants.NORTH</code>
 - * <li><code>SwingConstants.SOUTH</code>
 - * </ul>
 - * @param biasRet an array contain the bias that was checked
 - * @return the location within the model that best represents the next
 - * location visual position
 - * @exception BadLocationException
 - * @exception IllegalArgumentException if <code>direction</code> is invalid
 - */
 - static int getNextVisualPositionFrom(View v, int pos, Position.Bias b,
 - Shape alloc, int direction,
 - Position.Bias[] biasRet)
 - throws BadLocationException {
 - if (v.getViewCount() == 0) {
 - // Nothing to do.
 - return pos;
 - }
 - boolean top = (direction == SwingConstants.NORTH ||
 - direction == SwingConstants.WEST);
 - int retValue;
 - if (pos == -1) {
 - // Start from the first View.
 - int childIndex = (top) ? v.getViewCount() - 1 : 0;
 - View child = v.getView(childIndex);
 - Shape childBounds = v.getChildAllocation(childIndex, alloc);
 - retValue = child.getNextVisualPositionFrom(pos, b, childBounds,
 - direction, biasRet);
 - if (retValue == -1 && !top && v.getViewCount() > 1) {
 - // Special case that should ONLY happen if first view
 - // isn't valid (can happen when end position is put at
 - // beginning of line.
 - child = v.getView(1);
 - childBounds = v.getChildAllocation(1, alloc);
 - retValue = child.getNextVisualPositionFrom(-1, biasRet[0],
 - childBounds,
 - direction, biasRet);
 - }
 - }
 - else {
 - int increment = (top) ? -1 : 1;
 - int childIndex;
 - if (b == Position.Bias.Backward && pos > 0) {
 - childIndex = v.getViewIndex(pos - 1, Position.Bias.Forward);
 - }
 - else {
 - childIndex = v.getViewIndex(pos, Position.Bias.Forward);
 - }
 - View child = v.getView(childIndex);
 - Shape childBounds = v.getChildAllocation(childIndex, alloc);
 - retValue = child.getNextVisualPositionFrom(pos, b, childBounds,
 - direction, biasRet);
 - if ((direction == SwingConstants.EAST ||
 - direction == SwingConstants.WEST) &&
 - (v instanceof CompositeView) &&
 - ((CompositeView)v).flipEastAndWestAtEnds(pos, b)) {
 - increment *= -1;
 - }
 - childIndex += increment;
 - if (retValue == -1 && childIndex >= 0 &&
 - childIndex < v.getViewCount()) {
 - child = v.getView(childIndex);
 - childBounds = v.getChildAllocation(childIndex, alloc);
 - retValue = child.getNextVisualPositionFrom(
 - -1, b, childBounds, direction, biasRet);
 - // If there is a bias change, it is a fake position
 - // and we should skip it. This is usually the result
 - // of two elements side be side flowing the same way.
 - if (retValue == pos && biasRet[0] != b) {
 - return getNextVisualPositionFrom(v, pos, biasRet[0],
 - alloc, direction,
 - biasRet);
 - }
 - }
 - else if (retValue != -1 && biasRet[0] != b &&
 - ((increment == 1 && child.getEndOffset() == retValue) ||
 - (increment == -1 &&
 - child.getStartOffset() == retValue)) &&
 - childIndex >= 0 && childIndex < v.getViewCount()) {
 - // Reached the end of a view, make sure the next view
 - // is a different direction.
 - child = v.getView(childIndex);
 - childBounds = v.getChildAllocation(childIndex, alloc);
 - Position.Bias originalBias = biasRet[0];
 - int nextPos = child.getNextVisualPositionFrom(
 - -1, b, childBounds, direction, biasRet);
 - if (biasRet[0] == b) {
 - retValue = nextPos;
 - }
 - else {
 - biasRet[0] = originalBias;
 - }
 - }
 - }
 - return retValue;
 - }
 - }