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