Source for javax.swing.text.Utilities

   1: /* Utilities.java --
   2:    Copyright (C) 2004, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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.
  10: 
  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.
  15: 
  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.
  20: 
  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.
  25: 
  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. */
  37: 
  38: 
  39: package javax.swing.text;
  40: 
  41: import java.awt.FontMetrics;
  42: import java.awt.Graphics;
  43: import java.awt.Point;
  44: import java.text.BreakIterator;
  45: 
  46: import javax.swing.text.Position.Bias;
  47: 
  48: /**
  49:  * A set of utilities to deal with text. This is used by several other classes
  50:  * inside this package.
  51:  *
  52:  * @author Roman Kennke (roman@ontographics.com)
  53:  * @author Robert Schuster (robertschuster@fsfe.org)
  54:  */
  55: public class Utilities
  56: {
  57: 
  58:   /**
  59:    * Creates a new <code>Utilities</code> object.
  60:    */
  61:   public Utilities()
  62:   {
  63:     // Nothing to be done here.
  64:   }
  65: 
  66:   /**
  67:    * Draws the given text segment. Contained tabs and newline characters
  68:    * are taken into account. Tabs are expanded using the
  69:    * specified {@link TabExpander}.
  70:    *
  71:    *
  72:    * The X and Y coordinates denote the start of the <em>baseline</em> where
  73:    * the text should be drawn.
  74:    *
  75:    * @param s the text fragment to be drawn.
  76:    * @param x the x position for drawing.
  77:    * @param y the y position for drawing.
  78:    * @param g the {@link Graphics} context for drawing.
  79:    * @param e the {@link TabExpander} which specifies the Tab-expanding
  80:    *     technique.
  81:    * @param startOffset starting offset in the text.
  82:    * @return the x coordinate at the end of the drawn text.
  83:    */
  84:   public static final int drawTabbedText(Segment s, int x, int y, Graphics g,
  85:                                          TabExpander e, int startOffset)
  86:   {
  87:     // This buffers the chars to be drawn.
  88:     char[] buffer = s.array;
  89: 
  90:     // The font metrics of the current selected font.
  91:     FontMetrics metrics = g.getFontMetrics();
  92: 
  93:     int ascent = metrics.getAscent();
  94: 
  95:     // The current x and y pixel coordinates.
  96:     int pixelX = x;
  97: 
  98:     int pos = s.offset;
  99:     int len = 0;
 100:     
 101:     int end = s.offset + s.count;
 102: 
 103:     for (int offset = s.offset; offset < end; ++offset)
 104:       {
 105:         char c = buffer[offset];
 106:         switch (c)
 107:           {
 108:           case '\t':
 109:             if (len > 0) {
 110:               g.drawChars(buffer, pos, len, pixelX, y);
 111:               pixelX += metrics.charsWidth(buffer, pos, len);
 112:               len = 0;
 113:             }
 114:             pos = offset+1;
 115:             if (e != null)
 116:               pixelX = (int) e.nextTabStop((float) pixelX, startOffset + offset
 117:                                            - s.offset);
 118:             else
 119:               pixelX += metrics.charWidth(' ');
 120:             x = pixelX;
 121:             break;
 122:           case '\n':
 123:           case '\r':
 124:             if (len > 0) {
 125:               g.drawChars(buffer, pos, len, pixelX, y);
 126:               pixelX += metrics.charsWidth(buffer, pos, len);
 127:               len = 0;
 128:             }
 129:             x = pixelX;
 130:             break;
 131:           default:
 132:             len += 1;
 133:           }
 134:       }
 135: 
 136:     if (len > 0)
 137:       {
 138:         g.drawChars(buffer, pos, len, pixelX, y);
 139:         pixelX += metrics.charsWidth(buffer, pos, len);
 140:       }
 141:     
 142:     return pixelX;
 143:   }
 144: 
 145:   /**
 146:    * Determines the width, that the given text <code>s</code> would take
 147:    * if it was printed with the given {@link java.awt.FontMetrics} on the
 148:    * specified screen position.
 149:    * @param s the text fragment
 150:    * @param metrics the font metrics of the font to be used
 151:    * @param x the x coordinate of the point at which drawing should be done
 152:    * @param e the {@link TabExpander} to be used
 153:    * @param startOffset the index in <code>s</code> where to start
 154:    * @returns the width of the given text s. This takes tabs and newlines
 155:    * into account.
 156:    */
 157:   public static final int getTabbedTextWidth(Segment s, FontMetrics metrics,
 158:                                              int x, TabExpander e,
 159:                                              int startOffset)
 160:   {
 161:     // This buffers the chars to be drawn.
 162:     char[] buffer = s.array;
 163: 
 164:     // The current x coordinate.
 165:     int pixelX = x;
 166: 
 167:     // The current maximum width.
 168:     int maxWidth = 0;
 169: 
 170:     int end = s.offset + s.count;
 171:     int count = 0;
 172:     for (int offset = s.offset; offset < end; offset++)
 173:       {
 174:     switch (buffer[offset])
 175:       {
 176:       case '\t':
 177:         // In case we have a tab, we just 'jump' over the tab.
 178:         // When we have no tab expander we just use the width of 'm'.
 179:         if (e != null)
 180:           pixelX = (int) e.nextTabStop(pixelX,
 181:                        startOffset + offset - s.offset);
 182:         else
 183:           pixelX += metrics.charWidth(' ');
 184:         break;
 185:       case '\n':
 186:         // In case we have a newline, we must 'draw'
 187:         // the buffer and jump on the next line.
 188:         pixelX += metrics.charsWidth(buffer, offset - count, count);
 189:             count = 0;
 190:             break;
 191:           default:
 192:             count++;
 193:           }
 194:       }
 195: 
 196:     // Take the last line into account.
 197:     pixelX += metrics.charsWidth(buffer, end - count, count);
 198: 
 199:     return pixelX - x;
 200:   }
 201: 
 202:   /**
 203:    * Provides a facility to map screen coordinates into a model location. For a
 204:    * given text fragment and start location within this fragment, this method
 205:    * determines the model location so that the resulting fragment fits best
 206:    * into the span <code>[x0, x]</code>.
 207:    *
 208:    * The parameter <code>round</code> controls which model location is returned
 209:    * if the view coordinates are on a character: If <code>round</code> is
 210:    * <code>true</code>, then the result is rounded up to the next character, so
 211:    * that the resulting fragment is the smallest fragment that is larger than
 212:    * the specified span. If <code>round</code> is <code>false</code>, then the
 213:    * resulting fragment is the largest fragment that is smaller than the
 214:    * specified span.
 215:    *
 216:    * @param s the text segment
 217:    * @param fm the font metrics to use
 218:    * @param x0 the starting screen location
 219:    * @param x the target screen location at which the requested fragment should
 220:    *        end
 221:    * @param te the tab expander to use; if this is <code>null</code>, TABs are
 222:    *        expanded to one space character
 223:    * @param p0 the starting model location
 224:    * @param round if <code>true</code> round up to the next location, otherwise
 225:    *        round down to the current location
 226:    *
 227:    * @return the model location, so that the resulting fragment fits within the
 228:    *         specified span
 229:    */
 230:   public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
 231:                                               int x, TabExpander te, int p0,
 232:                                               boolean round)
 233:   {
 234:     int found = s.count;
 235:     int currentX = x0;
 236:     int nextX = currentX;
 237:     
 238:     int end = s.offset + s.count;
 239:     for (int pos = s.offset; pos < end && found == s.count; pos++)
 240:       {
 241:         char nextChar = s.array[pos];
 242:         
 243:         if (nextChar != '\t')
 244:           nextX += fm.charWidth(nextChar);
 245:         else
 246:           {
 247:             if (te == null)
 248:               nextX += fm.charWidth(' ');
 249:             else
 250:               nextX += ((int) te.nextTabStop(nextX, p0 + pos - s.offset));
 251:           }
 252:         
 253:         if (x >= currentX && x < nextX)
 254:           {
 255:             // Found position.
 256:             if ((! round) || ((x - currentX) < (nextX - x)))
 257:               {
 258:                 found = pos - s.offset;
 259:               }
 260:             else
 261:               {
 262:                 found = pos + 1 - s.offset;
 263:               }
 264:           }
 265:         currentX = nextX;
 266:       }
 267: 
 268:     return found;
 269:   }
 270: 
 271:   /**
 272:    * Provides a facility to map screen coordinates into a model location. For a
 273:    * given text fragment and start location within this fragment, this method
 274:    * determines the model location so that the resulting fragment fits best
 275:    * into the span <code>[x0, x]</code>.
 276:    *
 277:    * This method rounds up to the next location, so that the resulting fragment
 278:    * will be the smallest fragment of the text, that is greater than the
 279:    * specified span.
 280:    *
 281:    * @param s the text segment
 282:    * @param fm the font metrics to use
 283:    * @param x0 the starting screen location
 284:    * @param x the target screen location at which the requested fragment should
 285:    *        end
 286:    * @param te the tab expander to use; if this is <code>null</code>, TABs are
 287:    *        expanded to one space character
 288:    * @param p0 the starting model location
 289:    *
 290:    * @return the model location, so that the resulting fragment fits within the
 291:    *         specified span
 292:    */
 293:   public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
 294:                                               int x, TabExpander te, int p0)
 295:   {
 296:     return getTabbedTextOffset(s, fm, x0, x, te, p0, true);
 297:   }
 298:   
 299:   /**
 300:    * Finds the start of the next word for the given offset.
 301:    * 
 302:    * @param c
 303:    *          the text component
 304:    * @param offs
 305:    *          the offset in the document
 306:    * @return the location in the model of the start of the next word.
 307:    * @throws BadLocationException
 308:    *           if the offset is invalid.
 309:    */
 310:   public static final int getNextWord(JTextComponent c, int offs)
 311:       throws BadLocationException
 312:   {
 313:     if (offs < 0 || offs > (c.getText().length() - 1))
 314:       throw new BadLocationException("invalid offset specified", offs);
 315:     String text = c.getText();
 316:     BreakIterator wb = BreakIterator.getWordInstance();
 317:     wb.setText(text);
 318:         
 319:     int last = wb.following(offs);
 320:     int current = wb.next();
 321:     int cp;
 322: 
 323:     while (current != BreakIterator.DONE)
 324:       {
 325:         for (int i = last; i < current; i++)
 326:           {
 327:             cp = text.codePointAt(i);
 328:             
 329:             // Return the last found bound if there is a letter at the current
 330:             // location or is not whitespace (meaning it is a number or
 331:             // punctuation). The first case means that 'last' denotes the
 332:             // beginning of a word while the second case means it is the start
 333:             // of something else.
 334:             if (Character.isLetter(cp)
 335:                 || !Character.isWhitespace(cp))
 336:               return last;
 337:           }
 338:         last = current;
 339:         current = wb.next();
 340:       }
 341:     
 342:     throw new BadLocationException("no more words", offs);
 343:   }
 344: 
 345:   /**
 346:    * Finds the start of the previous word for the given offset.
 347:    * 
 348:    * @param c
 349:    *          the text component
 350:    * @param offs
 351:    *          the offset in the document
 352:    * @return the location in the model of the start of the previous word.
 353:    * @throws BadLocationException
 354:    *           if the offset is invalid.
 355:    */
 356:   public static final int getPreviousWord(JTextComponent c, int offs)
 357:       throws BadLocationException
 358:   {
 359:     String text = c.getText();
 360:     
 361:     if (offs <= 0 || offs > text.length())
 362:       throw new BadLocationException("invalid offset specified", offs);
 363:     
 364:     BreakIterator wb = BreakIterator.getWordInstance();
 365:     wb.setText(text);
 366:     int last = wb.preceding(offs);
 367:     int current = wb.previous();
 368:     int cp;
 369: 
 370:     while (current != BreakIterator.DONE)
 371:       {
 372:         for (int i = last; i < offs; i++)
 373:           {
 374:             cp = text.codePointAt(i);
 375:             
 376:             // Return the last found bound if there is a letter at the current
 377:             // location or is not whitespace (meaning it is a number or
 378:             // punctuation). The first case means that 'last' denotes the
 379:             // beginning of a word while the second case means it is the start
 380:             // of some else.
 381:             if (Character.isLetter(cp)
 382:                 || !Character.isWhitespace(cp))
 383:               return last;
 384:           }
 385:         last = current;
 386:         current = wb.previous();
 387:       }
 388:     
 389:     return 0;
 390:   }
 391:   
 392:   /**
 393:    * Finds the start of a word for the given location.
 394:    * @param c the text component
 395:    * @param offs the offset location
 396:    * @return the location of the word beginning
 397:    * @throws BadLocationException if the offset location is invalid
 398:    */
 399:   public static final int getWordStart(JTextComponent c, int offs)
 400:       throws BadLocationException
 401:   {
 402:     String text = c.getText();
 403:     
 404:     if (offs < 0 || offs > text.length())
 405:       throw new BadLocationException("invalid offset specified", offs);
 406:     
 407:     BreakIterator wb = BreakIterator.getWordInstance();
 408:     wb.setText(text);
 409: 
 410:     if (wb.isBoundary(offs))
 411:       return offs;
 412: 
 413:     return wb.preceding(offs);
 414:   }
 415:   
 416:   /**
 417:    * Finds the end of a word for the given location.
 418:    * @param c the text component
 419:    * @param offs the offset location
 420:    * @return the location of the word end
 421:    * @throws BadLocationException if the offset location is invalid
 422:    */
 423:   public static final int getWordEnd(JTextComponent c, int offs)
 424:       throws BadLocationException
 425:   {
 426:     if (offs < 0 || offs >= c.getText().length())
 427:       throw new BadLocationException("invalid offset specified", offs);
 428:     
 429:     String text = c.getText();
 430:     BreakIterator wb = BreakIterator.getWordInstance();
 431:     wb.setText(text);
 432:     return wb.following(offs);
 433:   }
 434:   
 435:   /**
 436:    * Get the model position of the end of the row that contains the 
 437:    * specified model position.  Return null if the given JTextComponent
 438:    * does not have a size.
 439:    * @param c the JTextComponent
 440:    * @param offs the model position
 441:    * @return the model position of the end of the row containing the given 
 442:    * offset
 443:    * @throws BadLocationException if the offset is invalid
 444:    */
 445:   public static final int getRowEnd(JTextComponent c, int offs)
 446:       throws BadLocationException
 447:   {
 448:     String text = c.getText();
 449:     if (text == null)
 450:       return -1;
 451: 
 452:     // Do a binary search for the smallest position X > offs
 453:     // such that that character at positino X is not on the same
 454:     // line as the character at position offs
 455:     int high = offs + ((text.length() - 1 - offs) / 2);
 456:     int low = offs;
 457:     int oldHigh = text.length() + 1;
 458:     while (true)
 459:       {
 460:         if (c.modelToView(high).y != c.modelToView(offs).y)
 461:           {
 462:             oldHigh = high;
 463:             high = low + ((high + 1 - low) / 2);
 464:             if (oldHigh == high)
 465:               return high - 1;
 466:           }
 467:         else
 468:           {
 469:             low = high;
 470:             high += ((oldHigh - high) / 2);
 471:             if (low == high)
 472:               return low;
 473:           }
 474:       }
 475:   }
 476:       
 477:   /**
 478:    * Get the model position of the start of the row that contains the specified
 479:    * model position. Return null if the given JTextComponent does not have a
 480:    * size.
 481:    * 
 482:    * @param c the JTextComponent
 483:    * @param offs the model position
 484:    * @return the model position of the start of the row containing the given
 485:    *         offset
 486:    * @throws BadLocationException if the offset is invalid
 487:    */
 488:   public static final int getRowStart(JTextComponent c, int offs)
 489:       throws BadLocationException
 490:   {
 491:     String text = c.getText();
 492:     if (text == null)
 493:       return -1;
 494: 
 495:     // Do a binary search for the greatest position X < offs
 496:     // such that the character at position X is not on the same
 497:     // row as the character at position offs
 498:     int high = offs;
 499:     int low = 0;
 500:     int oldLow = 0;
 501:     while (true)
 502:       {
 503:         if (c.modelToView(low).y != c.modelToView(offs).y)
 504:           {
 505:             oldLow = low;
 506:             low = high - ((high + 1 - low) / 2);
 507:             if (oldLow == low)
 508:               return low + 1;
 509:           }
 510:         else
 511:           {
 512:             high = low;
 513:             low -= ((low - oldLow) / 2);
 514:             if (low == high)
 515:               return low;
 516:           }
 517:       }
 518:   }
 519:   
 520:   /**
 521:    * Determine where to break the text in the given Segment, attempting to find
 522:    * a word boundary.
 523:    * @param s the Segment that holds the text
 524:    * @param metrics the font metrics used for calculating the break point
 525:    * @param x0 starting view location representing the start of the text
 526:    * @param x the target view location
 527:    * @param e the TabExpander used for expanding tabs (if this is null tabs
 528:    * are expanded to 1 space)
 529:    * @param startOffset the offset in the Document of the start of the text
 530:    * @return the offset at which we should break the text
 531:    */
 532:   public static final int getBreakLocation(Segment s, FontMetrics metrics,
 533:                                            int x0, int x, TabExpander e,
 534:                                            int startOffset)
 535:   {
 536:     int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset,
 537:                                              false);
 538:     int breakLoc = mark;
 539:     // If mark is equal to the end of the string, just use that position.
 540:     if (mark < s.count - 1)
 541:       {
 542:         for (int i = s.offset + mark; i >= s.offset; i--)
 543:           {
 544:             char ch = s.array[i];
 545:             if (ch < 256)
 546:               {
 547:                 // For ASCII simply scan backwards for whitespace.
 548:                 if (Character.isWhitespace(ch))
 549:                   {
 550:                     breakLoc = i - s.offset + 1;
 551:                     break;
 552:                   }
 553:               }
 554:             else
 555:               {
 556:                 // Only query BreakIterator for complex chars.
 557:                 BreakIterator bi = BreakIterator.getLineInstance();
 558:                 bi.setText(s);
 559:                 int pos = bi.preceding(i + 1);
 560:                 if (pos > s.offset)
 561:                   {
 562:                     breakLoc = breakLoc - s.offset;
 563:                   }
 564:                 break;
 565:               }
 566:           }
 567:       }
 568:     return breakLoc;
 569:   }
 570: 
 571:   /**
 572:    * Returns the paragraph element in the text component <code>c</code> at
 573:    * the specified location <code>offset</code>.
 574:    *
 575:    * @param c the text component
 576:    * @param offset the offset of the paragraph element to return
 577:    *
 578:    * @return the paragraph element at <code>offset</code>
 579:    */
 580:   public static final Element getParagraphElement(JTextComponent c, int offset)
 581:   {
 582:     Document doc = c.getDocument();
 583:     Element par = null;
 584:     if (doc instanceof StyledDocument)
 585:       {
 586:         StyledDocument styledDoc = (StyledDocument) doc;
 587:         par = styledDoc.getParagraphElement(offset);
 588:       }
 589:     else
 590:       {
 591:         Element root = c.getDocument().getDefaultRootElement();
 592:         int parIndex = root.getElementIndex(offset);
 593:         par = root.getElement(parIndex);
 594:       }
 595:     return par;
 596:   }
 597: 
 598:   /**
 599:    * Returns the document position that is closest above to the specified x
 600:    * coordinate in the row containing <code>offset</code>.
 601:    *
 602:    * @param c the text component
 603:    * @param offset the offset
 604:    * @param x the x coordinate
 605:    *
 606:    * @return  the document position that is closest above to the specified x
 607:    *          coordinate in the row containing <code>offset</code>
 608:    *
 609:    * @throws BadLocationException if <code>offset</code> is not a valid offset
 610:    */
 611:   public static final int getPositionAbove(JTextComponent c, int offset, int x)
 612:     throws BadLocationException
 613:   {
 614:     int offs = getRowStart(c, offset);
 615:     
 616:     if(offs == -1)
 617:       return -1;
 618: 
 619:     // Effectively calculates the y value of the previous line.
 620:     Point pt = c.modelToView(offs-1).getLocation();
 621:     
 622:     pt.x = x;
 623:     
 624:     // Calculate a simple fitting offset.
 625:     offs = c.viewToModel(pt);
 626:     
 627:     // Find out the real x positions of the calculated character and its
 628:     // neighbour.
 629:     int offsX = c.modelToView(offs).getLocation().x;
 630:     int offsXNext = c.modelToView(offs+1).getLocation().x;
 631:     
 632:     // Chose the one which is nearer to us and return its offset.
 633:     if (Math.abs(offsX-x) <= Math.abs(offsXNext-x))
 634:       return offs;
 635:     else
 636:       return offs+1;
 637:   }
 638: 
 639:   /**
 640:    * Returns the document position that is closest below to the specified x
 641:    * coordinate in the row containing <code>offset</code>.
 642:    *
 643:    * @param c the text component
 644:    * @param offset the offset
 645:    * @param x the x coordinate
 646:    *
 647:    * @return  the document position that is closest above to the specified x
 648:    *          coordinate in the row containing <code>offset</code>
 649:    *
 650:    * @throws BadLocationException if <code>offset</code> is not a valid offset
 651:    */
 652:   public static final int getPositionBelow(JTextComponent c, int offset, int x)
 653:     throws BadLocationException
 654:   {
 655:     int offs = getRowEnd(c, offset);
 656:     
 657:     if(offs == -1)
 658:       return -1;
 659: 
 660:     Point pt = null;
 661:     
 662:     // Note: Some views represent the position after the last
 663:     // typed character others do not. Converting offset 3 in "a\nb"
 664:     // in a PlainView will return a valid rectangle while in a
 665:     // WrappedPlainView this will throw a BadLocationException.
 666:     // This behavior has been observed in the RI.
 667:     try
 668:       {
 669:         // Effectively calculates the y value of the next line.
 670:         pt = c.modelToView(offs+1).getLocation();
 671:       }
 672:     catch(BadLocationException ble)
 673:       {
 674:         return offset;
 675:       }
 676:     
 677:     pt.x = x;
 678:     
 679:     // Calculate a simple fitting offset.
 680:     offs = c.viewToModel(pt);
 681:     
 682:     if (offs == c.getDocument().getLength())
 683:       return offs;
 684: 
 685:     // Find out the real x positions of the calculated character and its
 686:     // neighbour.
 687:     int offsX = c.modelToView(offs).getLocation().x;
 688:     int offsXNext = c.modelToView(offs+1).getLocation().x;
 689:     
 690:     // Chose the one which is nearer to us and return its offset.
 691:     if (Math.abs(offsX-x) <= Math.abs(offsXNext-x))
 692:       return offs;
 693:     else
 694:       return offs+1;
 695:     }
 696:   
 697:   /** This is an internal helper method which is used by the
 698:    * <code>javax.swing.text</code> package. It simply delegates the
 699:    * call to a method with the same name on the <code>NavigationFilter</code>
 700:    * of the provided <code>JTextComponent</code> (if it has one) or its UI.
 701:    * 
 702:    * If the underlying method throws a <code>BadLocationException</code> it
 703:    * will be swallowed and the initial offset is returned.
 704:    */
 705:   static int getNextVisualPositionFrom(JTextComponent t, int offset, int direction)
 706:   {
 707:     NavigationFilter nf = t.getNavigationFilter();
 708:     
 709:     try
 710:       {
 711:         return (nf != null) 
 712:           ? nf.getNextVisualPositionFrom(t,
 713:                                          offset,
 714:                                          Bias.Forward,
 715:                                          direction,
 716:                                          new Position.Bias[1])
 717:           : t.getUI().getNextVisualPositionFrom(t,
 718:                                                 offset,
 719:                                                 Bias.Forward,
 720:                                                 direction,
 721:                                                 new Position.Bias[1]);
 722:       }
 723:     catch (BadLocationException ble)
 724:     {
 725:       return offset;
 726:     }
 727:     
 728:   }
 729:   
 730: }