1 /*
   2  * Copyright (c) 1999, 2017, 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         float x = alloc.x;
 102         int p = v.getStartOffset();
 103         int[] justificationData = getJustificationData(v);
 104         if (p != p0) {
 105             text = v.getText(p, p0);
 106             float width = Utilities.getTabbedTextWidth(v, text, metrics, x,
 107                                                        expander, p,
 108                                                        justificationData);
 109             x += width;
 110             SegmentCache.releaseSharedSegment(text);
 111         }
 112 
 113         // determine the y coordinate to render the glyphs
 114         float y = alloc.y + metrics.getHeight() - metrics.getDescent();
 115 
 116         // render the glyphs
 117         text = v.getText(p0, p1);
 118         g.setFont(metrics.getFont());
 119 
 120         Utilities.drawTabbedText(v, text, x, y, g, expander,p0,
 121                                  justificationData, true);
 122         SegmentCache.releaseSharedSegment(text);
 123     }
 124 
 125     public Shape modelToView(GlyphView v, int pos, Position.Bias bias,
 126                              Shape a) throws BadLocationException {
 127 
 128         sync(v);
 129         Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 130         int p0 = v.getStartOffset();
 131         int p1 = v.getEndOffset();
 132         TabExpander expander = v.getTabExpander();
 133         Segment text;
 134 
 135         if(pos == p1) {
 136             // The caller of this is left to right and borders a right to
 137             // left view, return our end location.
 138             return new Rectangle(alloc.x + alloc.width, alloc.y, 0,
 139                                  metrics.getHeight());
 140         }
 141         if ((pos >= p0) && (pos <= p1)) {
 142             // determine range to the left of the position
 143             text = v.getText(p0, pos);
 144             int[] justificationData = getJustificationData(v);
 145             int width = Utilities.getTabbedTextWidth(v, text, metrics, alloc.x, expander, p0,
 146                                                      justificationData);
 147             SegmentCache.releaseSharedSegment(text);
 148             return new Rectangle(alloc.x + width, alloc.y, 0, metrics.getHeight());
 149         }
 150         throw new BadLocationException("modelToView - can't convert", p1);
 151     }
 152 
 153     /**
 154      * Provides a mapping from the view coordinate space to the logical
 155      * coordinate space of the model.
 156      *
 157      * @param v the view containing the view coordinates
 158      * @param x the X coordinate
 159      * @param y the Y coordinate
 160      * @param a the allocated region to render into
 161      * @param biasReturn always returns <code>Position.Bias.Forward</code>
 162      *   as the zero-th element of this array
 163      * @return the location within the model that best represents the
 164      *  given point in the view
 165      * @see View#viewToModel
 166      */
 167     public int viewToModel(GlyphView v, float x, float y, Shape a,
 168                            Position.Bias[] biasReturn) {
 169 
 170         sync(v);
 171         Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 172         int p0 = v.getStartOffset();
 173         int p1 = v.getEndOffset();
 174         TabExpander expander = v.getTabExpander();
 175         Segment text = v.getText(p0, p1);
 176         int[] justificationData = getJustificationData(v);
 177         int offs = Utilities.getTabbedTextOffset(v, text, metrics,
 178                                                  alloc.x, (int) x, expander, p0,
 179                                                  justificationData);
 180         SegmentCache.releaseSharedSegment(text);
 181         int retValue = p0 + offs;
 182         if(retValue == p1) {
 183             // No need to return backward bias as GlyphPainter1 is used for
 184             // ltr text only.
 185             retValue--;
 186         }
 187         biasReturn[0] = Position.Bias.Forward;
 188         return retValue;
 189     }
 190 
 191     /**
 192      * Determines the best location (in the model) to break
 193      * the given view.
 194      * This method attempts to break on a whitespace
 195      * location.  If a whitespace location can't be found, the
 196      * nearest character location is returned.
 197      *
 198      * @param v the view
 199      * @param p0 the location in the model where the
 200      *  fragment should start its representation >= 0
 201      * @param x the graphic location along the axis that the
 202      *  broken view would occupy >= 0; this may be useful for
 203      *  things like tab calculations
 204      * @param len specifies the distance into the view
 205      *  where a potential break is desired >= 0
 206      * @return the model location desired for a break
 207      * @see View#breakView
 208      */
 209     public int getBoundedPosition(GlyphView v, int p0, float x, float len) {
 210         sync(v);
 211         TabExpander expander = v.getTabExpander();
 212         Segment s = v.getText(p0, v.getEndOffset());
 213         int[] justificationData = getJustificationData(v);
 214         int index = Utilities.getTabbedTextOffset(v, s, metrics, x, (x+len),
 215                                                   expander, p0, false,
 216                                                   justificationData, true);
 217         SegmentCache.releaseSharedSegment(s);
 218         int p1 = p0 + index;
 219         return p1;
 220     }
 221 
 222     @SuppressWarnings("deprecation")
 223     void sync(GlyphView v) {
 224         Font f = v.getFont();
 225         if ((metrics == null) || (! f.equals(metrics.getFont()))) {
 226             // fetch a new FontMetrics
 227             Container c = v.getContainer();
 228             metrics = (c != null) ? c.getFontMetrics(f) :
 229                 Toolkit.getDefaultToolkit().getFontMetrics(f);
 230         }
 231     }
 232 
 233 
 234 
 235     /**
 236      * @return justificationData from the ParagraphRow this GlyphView
 237      * is in or {@code null} if no justification is needed
 238      */
 239     private int[] getJustificationData(GlyphView v) {
 240         View parent = v.getParent();
 241         int [] ret = null;
 242         if (parent instanceof ParagraphView.Row) {
 243             ParagraphView.Row row = ((ParagraphView.Row) parent);
 244             ret = row.justificationData;
 245         }
 246         return ret;
 247     }
 248 
 249     // --- variables ---------------------------------------------
 250 
 251     FontMetrics metrics;
 252 }