1 /*
   2  * Copyright (c) 1999, 2014, 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.awt.*;
  28 
  29 /**
  30  * A class to perform rendering of the glyphs.
  31  * This can be implemented to be stateless, or
  32  * to hold some information as a cache to
  33  * facilitate faster rendering and model/view
  34  * translation.  At a minimum, the GlyphPainter
  35  * allows a View implementation to perform its
  36  * duties independent of a particular version
  37  * of JVM and selection of capabilities (i.e.
  38  * shaping for i18n, etc).
  39  * <p>
  40  * This implementation is intended for operation
  41  * under the JDK1.1 API of the Java Platform.
  42  * Since the JDK is backward compatible with
  43  * JDK1.1 API, this class will also function on
  44  * Java 2.  The JDK introduces improved
  45  * API for rendering text however, so the GlyphPainter2
  46  * is recommended for the DK.
  47  *
  48  * @author  Timothy Prinzing
  49  * @see GlyphView
  50  */
  51 class GlyphPainter1 extends GlyphView.GlyphPainter {
  52 
  53     /**
  54      * Determine the span the glyphs given a start location
  55      * (for tab expansion).
  56      */
  57     public float getSpan(GlyphView v, int p0, int p1,
  58                          TabExpander e, float x) {
  59         sync(v);
  60         Segment text = v.getText(p0, p1);
  61         int[] justificationData = getJustificationData(v);
  62         int width = Utilities.getTabbedTextWidth(v, text, metrics, (int) x, e, p0,
  63                                                  justificationData);
  64         SegmentCache.releaseSharedSegment(text);
  65         return width;
  66     }
  67 
  68     public float getHeight(GlyphView v) {
  69         sync(v);
  70         return metrics.getHeight();
  71     }
  72 
  73     /**
  74      * Fetches the ascent above the baseline for the glyphs
  75      * corresponding to the given range in the model.
  76      */
  77     public float getAscent(GlyphView v) {
  78         sync(v);
  79         return metrics.getAscent();
  80     }
  81 
  82     /**
  83      * Fetches the descent below the baseline for the glyphs
  84      * corresponding to the given range in the model.
  85      */
  86     public float getDescent(GlyphView v) {
  87         sync(v);
  88         return metrics.getDescent();
  89     }
  90 
  91     /**
  92      * Paints the glyphs representing the given range.
  93      */
  94     public void paint(GlyphView v, Graphics g, Shape a, int p0, int p1) {
  95         sync(v);
  96         Segment text;
  97         TabExpander expander = v.getTabExpander();
  98         Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
  99 
 100         // determine the x coordinate to render the glyphs
 101         int x = alloc.x;
 102         int p = v.getStartOffset();
 103         int[] justificationData = getJustificationData(v);
 104         if (p != p0) {
 105             text = v.getText(p, p0);
 106             int width = Utilities.getTabbedTextWidth(v, text, metrics, x, expander, p,
 107                                                      justificationData);
 108             x += width;
 109             SegmentCache.releaseSharedSegment(text);
 110         }
 111 
 112         // determine the y coordinate to render the glyphs
 113         int y = alloc.y + metrics.getHeight() - metrics.getDescent();
 114 
 115         // render the glyphs
 116         text = v.getText(p0, p1);
 117         g.setFont(metrics.getFont());
 118 
 119         Utilities.drawTabbedText(v, text, x, y, g, expander,p0,
 120                                  justificationData);
 121         SegmentCache.releaseSharedSegment(text);
 122     }
 123 
 124     public Shape modelToView(GlyphView v, int pos, Position.Bias bias,
 125                              Shape a) throws BadLocationException {
 126 
 127         sync(v);
 128         Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 129         int p0 = v.getStartOffset();
 130         int p1 = v.getEndOffset();
 131         TabExpander expander = v.getTabExpander();
 132         Segment text;
 133 
 134         if(pos == p1) {
 135             // The caller of this is left to right and borders a right to
 136             // left view, return our end location.
 137             return new Rectangle(alloc.x + alloc.width, alloc.y, 0,
 138                                  metrics.getHeight());
 139         }
 140         if ((pos >= p0) && (pos <= p1)) {
 141             // determine range to the left of the position
 142             text = v.getText(p0, pos);
 143             int[] justificationData = getJustificationData(v);
 144             int width = Utilities.getTabbedTextWidth(v, text, metrics, alloc.x, expander, p0,
 145                                                      justificationData);
 146             SegmentCache.releaseSharedSegment(text);
 147             return new Rectangle(alloc.x + width, alloc.y, 0, metrics.getHeight());
 148         }
 149         throw new BadLocationException("modelToView - can't convert", p1);
 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 always returns <code>Position.Bias.Forward</code>
 161      *   as the zero-th element of this array
 162      * @return the location within the model that best represents the
 163      *  given point in the view
 164      * @see View#viewToModel
 165      */
 166     public int viewToModel(GlyphView v, float x, float y, Shape a,
 167                            Position.Bias[] biasReturn) {
 168 
 169         sync(v);
 170         Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 171         int p0 = v.getStartOffset();
 172         int p1 = v.getEndOffset();
 173         TabExpander expander = v.getTabExpander();
 174         Segment text = v.getText(p0, p1);
 175         int[] justificationData = getJustificationData(v);
 176         int offs = Utilities.getTabbedTextOffset(v, text, metrics,
 177                                                  alloc.x, (int) x, expander, p0,
 178                                                  justificationData);
 179         SegmentCache.releaseSharedSegment(text);
 180         int retValue = p0 + offs;
 181         if(retValue == p1) {
 182             // No need to return backward bias as GlyphPainter1 is used for
 183             // ltr text only.
 184             retValue--;
 185         }
 186         biasReturn[0] = Position.Bias.Forward;
 187         return retValue;
 188     }
 189 
 190     /**
 191      * Determines the best location (in the model) to break
 192      * the given view.
 193      * This method attempts to break on a whitespace
 194      * location.  If a whitespace location can't be found, the
 195      * nearest character location is returned.
 196      *
 197      * @param v the view
 198      * @param p0 the location in the model where the
 199      *  fragment should start its representation >= 0
 200      * @param pos the graphic location along the axis that the
 201      *  broken view would occupy >= 0; this may be useful for
 202      *  things like tab calculations
 203      * @param len specifies the distance into the view
 204      *  where a potential break is desired >= 0
 205      * @return the model location desired for a break
 206      * @see View#breakView
 207      */
 208     public int getBoundedPosition(GlyphView v, int p0, float x, float len) {
 209         sync(v);
 210         TabExpander expander = v.getTabExpander();
 211         Segment s = v.getText(p0, v.getEndOffset());
 212         int[] justificationData = getJustificationData(v);
 213         int index = Utilities.getTabbedTextOffset(v, s, metrics, (int)x, (int)(x+len),
 214                                                   expander, p0, false,
 215                                                   justificationData);
 216         SegmentCache.releaseSharedSegment(s);
 217         int p1 = p0 + index;
 218         return p1;
 219     }
 220 
 221     @SuppressWarnings("deprecation")
 222     void sync(GlyphView v) {
 223         Font f = v.getFont();
 224         if ((metrics == null) || (! f.equals(metrics.getFont()))) {
 225             // fetch a new FontMetrics
 226             Container c = v.getContainer();
 227             metrics = (c != null) ? c.getFontMetrics(f) :
 228                 Toolkit.getDefaultToolkit().getFontMetrics(f);
 229         }
 230     }
 231 
 232 
 233 
 234     /**
 235      * @return justificationData from the ParagraphRow this GlyphView
 236      * is in or {@code null} if no justification is needed
 237      */
 238     private int[] getJustificationData(GlyphView v) {
 239         View parent = v.getParent();
 240         int [] ret = null;
 241         if (parent instanceof ParagraphView.Row) {
 242             ParagraphView.Row row = ((ParagraphView.Row) parent);
 243             ret = row.justificationData;
 244         }
 245         return ret;
 246     }
 247 
 248     // --- variables ---------------------------------------------
 249 
 250     FontMetrics metrics;
 251 }