1 /* 2 * Copyright (c) 1997, 2013, 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 import javax.swing.*; 29 import javax.swing.event.*; 30 31 /** 32 * Extends the multi-line plain text view to be suitable 33 * for a single-line editor view. If the view is 34 * allocated extra space, the field must adjust for it. 35 * If the hosting component is a JTextField, this view 36 * will manage the ranges of the associated BoundedRangeModel 37 * and will adjust the horizontal allocation to match the 38 * current visibility settings of the JTextField. 39 * 40 * @author Timothy Prinzing 41 * @see View 42 */ 43 public class FieldView extends PlainView { 44 45 /** 46 * Constructs a new FieldView wrapped on an element. 47 * 48 * @param elem the element 49 */ 50 public FieldView(Element elem) { 51 super(elem); 52 } 53 54 /** 55 * Fetches the font metrics associated with the component hosting 56 * this view. 57 * 58 * @return the metrics 59 */ 60 protected FontMetrics getFontMetrics() { 61 Component c = getContainer(); 62 return c.getFontMetrics(c.getFont()); 63 } 64 65 /** 66 * Adjusts the allocation given to the view 67 * to be a suitable allocation for a text field. 68 * If the view has been allocated more than the 69 * preferred span vertically, the allocation is 70 * changed to be centered vertically. Horizontally 71 * the view is adjusted according to the horizontal 72 * alignment property set on the associated JTextField 73 * (if that is the type of the hosting component). 74 * 75 * @param a the allocation given to the view, which may need 76 * to be adjusted. 77 * @return the allocation that the superclass should use. 78 */ 79 protected Shape adjustAllocation(Shape a) { 80 if (a != null) { 81 Rectangle bounds = a.getBounds(); 82 int vspan = (int) getPreferredSpan(Y_AXIS); 83 int hspan = (int) getPreferredSpan(X_AXIS); 84 if (bounds.height != vspan) { 85 int slop = bounds.height - vspan; 86 bounds.y += slop / 2; 87 bounds.height -= slop; 88 } 89 90 // horizontal adjustments 91 Component c = getContainer(); 92 if (c instanceof JTextField) { 93 JTextField field = (JTextField) c; 94 BoundedRangeModel vis = field.getHorizontalVisibility(); 95 int max = Math.max(hspan, bounds.width); 96 int value = vis.getValue(); 97 int extent = Math.min(max, bounds.width - 1); 98 if ((value + extent) > max) { 99 value = max - extent; 100 } 101 vis.setRangeProperties(value, extent, vis.getMinimum(), 102 max, false); 103 if (hspan < bounds.width) { 104 // horizontally align the interior 105 int slop = bounds.width - 1 - hspan; 106 107 int align = ((JTextField)c).getHorizontalAlignment(); 108 if(Utilities.isLeftToRight(c)) { 109 if(align==LEADING) { 110 align = LEFT; 111 } 112 else if(align==TRAILING) { 113 align = RIGHT; 114 } 115 } 116 else { 117 if(align==LEADING) { 118 align = RIGHT; 119 } 120 else if(align==TRAILING) { 121 align = LEFT; 122 } 123 } 124 125 switch (align) { 126 case SwingConstants.CENTER: 127 bounds.x += slop / 2; 128 bounds.width -= slop; 129 break; 130 case SwingConstants.RIGHT: 131 bounds.x += slop; 132 bounds.width -= slop; 133 break; 134 } 135 } else { 136 // adjust the allocation to match the bounded range. 137 bounds.width = hspan; 138 bounds.x -= vis.getValue(); 139 } 140 } 141 return bounds; 142 } 143 return null; 144 } 145 146 /** 147 * Update the visibility model with the associated JTextField 148 * (if there is one) to reflect the current visibility as a 149 * result of changes to the document model. The bounded 150 * range properties are updated. If the view hasn't yet been 151 * shown the extent will be zero and we just set it to be full 152 * until determined otherwise. 153 */ 154 void updateVisibilityModel() { 155 Component c = getContainer(); 156 if (c instanceof JTextField) { 157 JTextField field = (JTextField) c; 158 BoundedRangeModel vis = field.getHorizontalVisibility(); 159 int hspan = (int) getPreferredSpan(X_AXIS); 160 int extent = vis.getExtent(); 161 int maximum = Math.max(hspan, extent); 162 extent = (extent == 0) ? maximum : extent; 163 int value = maximum - extent; 164 int oldValue = vis.getValue(); 165 if ((oldValue + extent) > maximum) { 166 oldValue = maximum - extent; 167 } 168 value = Math.max(0, Math.min(value, oldValue)); 169 vis.setRangeProperties(value, extent, 0, maximum, false); 170 } 171 } 172 173 // --- View methods ------------------------------------------- 174 175 /** 176 * Renders using the given rendering surface and area on that surface. 177 * The view may need to do layout and create child views to enable 178 * itself to render into the given allocation. 179 * 180 * @param g the rendering surface to use 181 * @param a the allocated region to render into 182 * 183 * @see View#paint 184 */ 185 public void paint(Graphics g, Shape a) { 186 Rectangle r = (Rectangle) a; 187 g.clipRect(r.x, r.y, r.width, r.height); 188 super.paint(g, a); 189 } 190 191 /** 192 * Adjusts <code>a</code> based on the visible region and returns it. 193 */ 194 Shape adjustPaintRegion(Shape a) { 195 return adjustAllocation(a); 196 } 197 198 /** 199 * Determines the preferred span for this view along an 200 * axis. 201 * 202 * @param axis may be either View.X_AXIS or View.Y_AXIS 203 * @return the span the view would like to be rendered into >= 0. 204 * Typically the view is told to render into the span 205 * that is returned, although there is no guarantee. 206 * The parent may choose to resize or break the view. 207 */ 208 public float getPreferredSpan(int axis) { 209 switch (axis) { 210 case View.X_AXIS: 211 Segment buff = SegmentCache.getSharedSegment(); 212 Document doc = getDocument(); 213 int width; 214 try { 215 FontMetrics fm = getFontMetrics(); 216 doc.getText(0, doc.getLength(), buff); 217 width = Utilities.getTabbedTextWidth(buff, fm, 0, this, 0); 218 if (buff.count > 0) { 219 Component c = getContainer(); 220 firstLineOffset = sun.swing.SwingUtilities2. 221 getLeftSideBearing((c instanceof JComponent) ? 222 (JComponent)c : null, fm, 223 buff.array[buff.offset]); 224 firstLineOffset = Math.max(0, -firstLineOffset); 225 } 226 else { 227 firstLineOffset = 0; 228 } 229 } catch (BadLocationException bl) { 230 width = 0; 231 } 232 SegmentCache.releaseSharedSegment(buff); 233 return width + firstLineOffset; 234 default: 235 return super.getPreferredSpan(axis); 236 } 237 } 238 239 /** 240 * Determines the resizability of the view along the 241 * given axis. A value of 0 or less is not resizable. 242 * 243 * @param axis View.X_AXIS or View.Y_AXIS 244 * @return the weight -> 1 for View.X_AXIS, else 0 245 */ 246 public int getResizeWeight(int axis) { 247 if (axis == View.X_AXIS) { 248 return 1; 249 } 250 return 0; 251 } 252 253 /** 254 * Provides a mapping from the document model coordinate space 255 * to the coordinate space of the view mapped to it. 256 * 257 * @param pos the position to convert >= 0 258 * @param a the allocated region to render into 259 * @return the bounding box of the given position 260 * @exception BadLocationException if the given position does not 261 * represent a valid location in the associated document 262 * @see View#modelToView 263 */ 264 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { 265 return super.modelToView(pos, adjustAllocation(a), b); 266 } 267 268 /** 269 * Provides a mapping from the view coordinate space to the logical 270 * coordinate space of the model. 271 * 272 * @param fx the X coordinate >= 0.0f 273 * @param fy the Y coordinate >= 0.0f 274 * @param a the allocated region to render into 275 * @return the location within the model that best represents the 276 * given point in the view 277 * @see View#viewToModel 278 */ 279 public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) { 280 return super.viewToModel(fx, fy, adjustAllocation(a), bias); 281 } 282 283 /** 284 * Gives notification that something was inserted into the document 285 * in a location that this view is responsible for. 286 * 287 * @param changes the change information from the associated document 288 * @param a the current allocation of the view 289 * @param f the factory to use to rebuild if the view has children 290 * @see View#insertUpdate 291 */ 292 public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) { 293 super.insertUpdate(changes, adjustAllocation(a), f); 294 updateVisibilityModel(); 295 } 296 297 /** 298 * Gives notification that something was removed from the document 299 * in a location that this view is responsible for. 300 * 301 * @param changes the change information from the associated document 302 * @param a the current allocation of the view 303 * @param f the factory to use to rebuild if the view has children 304 * @see View#removeUpdate 305 */ 306 public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) { 307 super.removeUpdate(changes, adjustAllocation(a), f); 308 updateVisibilityModel(); 309 } 310 311 }