Home » openjdk-7 » javax » swing » text » [javadoc | source]

    1   /*
    2    * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package javax.swing.text;
   26   
   27   import java.text;
   28   import java.awt;
   29   import java.awt.font;
   30   import java.awt.geom.Rectangle2D;
   31   
   32   /**
   33    * A class to perform rendering of the glyphs.
   34    * This can be implemented to be stateless, or
   35    * to hold some information as a cache to
   36    * facilitate faster rendering and model/view
   37    * translation.  At a minimum, the GlyphPainter
   38    * allows a View implementation to perform its
   39    * duties independent of a particular version
   40    * of JVM and selection of capabilities (i.e.
   41    * shaping for i18n, etc).
   42    * <p>
   43    * This implementation is intended for operation
   44    * under the JDK.  It uses the
   45    * java.awt.font.TextLayout class to do i18n capable
   46    * rendering.
   47    *
   48    * @author  Timothy Prinzing
   49    * @see GlyphView
   50    */
   51   class GlyphPainter2 extends GlyphView.GlyphPainter {
   52   
   53       public GlyphPainter2(TextLayout layout) {
   54           this.layout = layout;
   55       }
   56   
   57       /**
   58        * Create a painter to use for the given GlyphView.
   59        */
   60       public GlyphView.GlyphPainter getPainter(GlyphView v, int p0, int p1) {
   61           return null;
   62       }
   63   
   64       /**
   65        * Determine the span the glyphs given a start location
   66        * (for tab expansion).  This implementation assumes it
   67        * has no tabs (i.e. TextLayout doesn't deal with tab
   68        * expansion).
   69        */
   70       public float getSpan(GlyphView v, int p0, int p1,
   71                            TabExpander e, float x) {
   72   
   73           if ((p0 == v.getStartOffset()) && (p1 == v.getEndOffset())) {
   74               return layout.getAdvance();
   75           }
   76           int p = v.getStartOffset();
   77           int index0 = p0 - p;
   78           int index1 = p1 - p;
   79   
   80           TextHitInfo hit0 = TextHitInfo.afterOffset(index0);
   81           TextHitInfo hit1 = TextHitInfo.beforeOffset(index1);
   82           float[] locs = layout.getCaretInfo(hit0);
   83           float x0 = locs[0];
   84           locs = layout.getCaretInfo(hit1);
   85           float x1 = locs[0];
   86           return (x1 > x0) ? x1 - x0 : x0 - x1;
   87       }
   88   
   89       public float getHeight(GlyphView v) {
   90           return layout.getAscent() + layout.getDescent() + layout.getLeading();
   91       }
   92   
   93       /**
   94        * Fetch the ascent above the baseline for the glyphs
   95        * corresponding to the given range in the model.
   96        */
   97       public float getAscent(GlyphView v) {
   98           return layout.getAscent();
   99       }
  100   
  101       /**
  102        * Fetch the descent below the baseline for the glyphs
  103        * corresponding to the given range in the model.
  104        */
  105       public float getDescent(GlyphView v) {
  106           return layout.getDescent();
  107       }
  108   
  109       /**
  110        * Paint the glyphs for the given view.  This is implemented
  111        * to only render if the Graphics is of type Graphics2D which
  112        * is required by TextLayout (and this should be the case if
  113        * running on the JDK).
  114        */
  115       public void paint(GlyphView v, Graphics g, Shape a, int p0, int p1) {
  116           if (g instanceof Graphics2D) {
  117               Rectangle2D alloc = a.getBounds2D();
  118               Graphics2D g2d = (Graphics2D)g;
  119               float y = (float) alloc.getY() + layout.getAscent() + layout.getLeading();
  120               float x = (float) alloc.getX();
  121               if( p0 > v.getStartOffset() || p1 < v.getEndOffset() ) {
  122                   try {
  123                       //TextLayout can't render only part of it's range, so if a
  124                       //partial range is required, add a clip region.
  125                       Shape s = v.modelToView(p0, Position.Bias.Forward,
  126                                               p1, Position.Bias.Backward, a);
  127                       Shape savedClip = g.getClip();
  128                       g2d.clip(s);
  129                       layout.draw(g2d, x, y);
  130                       g.setClip(savedClip);
  131                   } catch (BadLocationException e) {}
  132               } else {
  133                   layout.draw(g2d, x, y);
  134               }
  135           }
  136       }
  137   
  138       public Shape modelToView(GlyphView v, int pos, Position.Bias bias,
  139                                Shape a) throws BadLocationException {
  140           int offs = pos - v.getStartOffset();
  141           Rectangle2D alloc = a.getBounds2D();
  142           TextHitInfo hit = (bias == Position.Bias.Forward) ?
  143               TextHitInfo.afterOffset(offs) : TextHitInfo.beforeOffset(offs);
  144           float[] locs = layout.getCaretInfo(hit);
  145   
  146           // vertical at the baseline, should use slope and check if glyphs
  147           // are being rendered vertically.
  148           alloc.setRect(alloc.getX() + locs[0], alloc.getY(), 1, alloc.getHeight());
  149           return alloc;
  150       }
  151   
  152       /**
  153        * Provides a mapping from the view coordinate space to the logical
  154        * coordinate space of the model.
  155        *
  156        * @param v the view containing the view coordinates
  157        * @param x the X coordinate
  158        * @param y the Y coordinate
  159        * @param a the allocated region to render into
  160        * @param biasReturn either <code>Position.Bias.Forward</code>
  161        *  or <code>Position.Bias.Backward</code> is returned as the
  162        *  zero-th element of this array
  163        * @return the location within the model that best represents the
  164        *  given point of view
  165        * @see View#viewToModel
  166        */
  167       public int viewToModel(GlyphView v, float x, float y, Shape a,
  168                              Position.Bias[] biasReturn) {
  169   
  170           Rectangle2D alloc = (a instanceof Rectangle2D) ? (Rectangle2D)a : a.getBounds2D();
  171           //Move the y co-ord of the hit onto the baseline.  This is because TextLayout supports
  172           //italic carets and we do not.
  173           TextHitInfo hit = layout.hitTestChar(x - (float)alloc.getX(), 0);
  174           int pos = hit.getInsertionIndex();
  175   
  176           if (pos == v.getEndOffset()) {
  177               pos--;
  178           }
  179   
  180           biasReturn[0] = hit.isLeadingEdge() ? Position.Bias.Forward : Position.Bias.Backward;
  181           return pos + v.getStartOffset();
  182       }
  183   
  184       /**
  185        * Determines the model location that represents the
  186        * maximum advance that fits within the given span.
  187        * This could be used to break the given view.  The result
  188        * should be a location just shy of the given advance.  This
  189        * differs from viewToModel which returns the closest
  190        * position which might be proud of the maximum advance.
  191        *
  192        * @param v the view to find the model location to break at.
  193        * @param p0 the location in the model where the
  194        *  fragment should start it's representation >= 0.
  195        * @param pos the graphic location along the axis that the
  196        *  broken view would occupy >= 0.  This may be useful for
  197        *  things like tab calculations.
  198        * @param len specifies the distance into the view
  199        *  where a potential break is desired >= 0.
  200        * @return the maximum model location possible for a break.
  201        * @see View#breakView
  202        */
  203       public int getBoundedPosition(GlyphView v, int p0, float x, float len) {
  204           if( len < 0 )
  205               throw new IllegalArgumentException("Length must be >= 0.");
  206           // note: this only works because swing uses TextLayouts that are
  207           // only pure rtl or pure ltr
  208           TextHitInfo hit;
  209           if (layout.isLeftToRight()) {
  210               hit = layout.hitTestChar(len, 0);
  211           } else {
  212               hit = layout.hitTestChar(layout.getAdvance() - len, 0);
  213           }
  214           return v.getStartOffset() + hit.getCharIndex();
  215       }
  216   
  217       /**
  218            * Provides a way to determine the next visually represented model
  219            * location that one might place a caret.  Some views may not be
  220            * visible, they might not be in the same order found in the model, or
  221            * they just might not allow access to some of the locations in the
  222            * model.
  223            *
  224            * @param v the view to use
  225            * @param pos the position to convert >= 0
  226            * @param a the allocated region to render into
  227            * @param direction the direction from the current position that can
  228            *  be thought of as the arrow keys typically found on a keyboard.
  229            *  This may be SwingConstants.WEST, SwingConstants.EAST,
  230            *  SwingConstants.NORTH, or SwingConstants.SOUTH.
  231            * @return the location within the model that best represents the next
  232            *  location visual position.
  233            * @exception BadLocationException
  234            * @exception IllegalArgumentException for an invalid direction
  235            */
  236           public int getNextVisualPositionFrom(GlyphView v, int pos,
  237                                                Position.Bias b, Shape a,
  238                                                int direction,
  239                                                Position.Bias[] biasRet)
  240               throws BadLocationException {
  241   
  242               int startOffset = v.getStartOffset();
  243               int endOffset = v.getEndOffset();
  244               Segment text;
  245               AbstractDocument doc;
  246               boolean viewIsLeftToRight;
  247               TextHitInfo currentHit, nextHit;
  248   
  249               switch (direction) {
  250               case View.NORTH:
  251                   break;
  252               case View.SOUTH:
  253                   break;
  254               case View.EAST:
  255                   doc = (AbstractDocument)v.getDocument();
  256                   viewIsLeftToRight = doc.isLeftToRight(startOffset, endOffset);
  257   
  258                   if(startOffset == doc.getLength()) {
  259                       if(pos == -1) {
  260                           biasRet[0] = Position.Bias.Forward;
  261                           return startOffset;
  262                       }
  263                       // End case for bidi text where newline is at beginning
  264                       // of line.
  265                       return -1;
  266                   }
  267                   if(pos == -1) {
  268                       // Entering view from the left.
  269                       if( viewIsLeftToRight ) {
  270                           biasRet[0] = Position.Bias.Forward;
  271                           return startOffset;
  272                       } else {
  273                           text = v.getText(endOffset - 1, endOffset);
  274                           char c = text.array[text.offset];
  275                           SegmentCache.releaseSharedSegment(text);
  276                           if(c == '\n') {
  277                               biasRet[0] = Position.Bias.Forward;
  278                               return endOffset-1;
  279                           }
  280                           biasRet[0] = Position.Bias.Backward;
  281                           return endOffset;
  282                       }
  283                   }
  284                   if( b==Position.Bias.Forward )
  285                       currentHit = TextHitInfo.afterOffset(pos-startOffset);
  286                   else
  287                       currentHit = TextHitInfo.beforeOffset(pos-startOffset);
  288                   nextHit = layout.getNextRightHit(currentHit);
  289                   if( nextHit == null ) {
  290                       return -1;
  291                   }
  292                   if( viewIsLeftToRight != layout.isLeftToRight() ) {
  293                       // If the layout's base direction is different from
  294                       // this view's run direction, we need to use the weak
  295                       // carrat.
  296                       nextHit = layout.getVisualOtherHit(nextHit);
  297                   }
  298                   pos = nextHit.getInsertionIndex() + startOffset;
  299   
  300                   if(pos == endOffset) {
  301                       // A move to the right from an internal position will
  302                       // only take us to the endOffset in a left to right run.
  303                       text = v.getText(endOffset - 1, endOffset);
  304                       char c = text.array[text.offset];
  305                       SegmentCache.releaseSharedSegment(text);
  306                       if(c == '\n') {
  307                           return -1;
  308                       }
  309                       biasRet[0] = Position.Bias.Backward;
  310                   }
  311                   else {
  312                       biasRet[0] = Position.Bias.Forward;
  313                   }
  314                   return pos;
  315               case View.WEST:
  316                   doc = (AbstractDocument)v.getDocument();
  317                   viewIsLeftToRight = doc.isLeftToRight(startOffset, endOffset);
  318   
  319                   if(startOffset == doc.getLength()) {
  320                       if(pos == -1) {
  321                           biasRet[0] = Position.Bias.Forward;
  322                           return startOffset;
  323                       }
  324                       // End case for bidi text where newline is at beginning
  325                       // of line.
  326                       return -1;
  327                   }
  328                   if(pos == -1) {
  329                       // Entering view from the right
  330                       if( viewIsLeftToRight ) {
  331                           text = v.getText(endOffset - 1, endOffset);
  332                           char c = text.array[text.offset];
  333                           SegmentCache.releaseSharedSegment(text);
  334                           if ((c == '\n') || Character.isSpaceChar(c)) {
  335                               biasRet[0] = Position.Bias.Forward;
  336                               return endOffset - 1;
  337                           }
  338                           biasRet[0] = Position.Bias.Backward;
  339                           return endOffset;
  340                       } else {
  341                           biasRet[0] = Position.Bias.Forward;
  342                           return startOffset;
  343                      }
  344                   }
  345                   if( b==Position.Bias.Forward )
  346                       currentHit = TextHitInfo.afterOffset(pos-startOffset);
  347                   else
  348                       currentHit = TextHitInfo.beforeOffset(pos-startOffset);
  349                   nextHit = layout.getNextLeftHit(currentHit);
  350                   if( nextHit == null ) {
  351                       return -1;
  352                   }
  353                   if( viewIsLeftToRight != layout.isLeftToRight() ) {
  354                       // If the layout's base direction is different from
  355                       // this view's run direction, we need to use the weak
  356                       // carrat.
  357                       nextHit = layout.getVisualOtherHit(nextHit);
  358                   }
  359                   pos = nextHit.getInsertionIndex() + startOffset;
  360   
  361                   if(pos == endOffset) {
  362                       // A move to the left from an internal position will
  363                       // only take us to the endOffset in a right to left run.
  364                       text = v.getText(endOffset - 1, endOffset);
  365                       char c = text.array[text.offset];
  366                       SegmentCache.releaseSharedSegment(text);
  367                       if(c == '\n') {
  368                           return -1;
  369                       }
  370                       biasRet[0] = Position.Bias.Backward;
  371                   }
  372                   else {
  373                       biasRet[0] = Position.Bias.Forward;
  374                   }
  375                   return pos;
  376               default:
  377                   throw new IllegalArgumentException("Bad direction: " + direction);
  378               }
  379               return pos;
  380   
  381           }
  382       // --- variables ---------------------------------------------
  383   
  384       TextLayout layout;
  385   
  386   }

Home » openjdk-7 » javax » swing » text » [javadoc | source]