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