1 /* 2 * Copyright (c) 1997, 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.plaf.basic; 26 27 import java.beans.*; 28 import java.awt.*; 29 import java.awt.event.KeyEvent; 30 import java.awt.event.InputEvent; 31 import javax.swing.*; 32 import javax.swing.event.DocumentEvent; 33 import javax.swing.text.*; 34 import javax.swing.plaf.*; 35 36 /** 37 * Provides the look and feel for a plain text editor. In this 38 * implementation the default UI is extended to act as a simple 39 * view factory. 40 * <p> 41 * <strong>Warning:</strong> 42 * Serialized objects of this class will not be compatible with 43 * future Swing releases. The current serialization support is 44 * appropriate for short term storage or RMI between applications running 45 * the same version of Swing. As of 1.4, support for long term storage 46 * of all JavaBeans™ 47 * has been added to the <code>java.beans</code> package. 48 * Please see {@link java.beans.XMLEncoder}. 49 * 50 * @author Timothy Prinzing 51 */ 52 @SuppressWarnings("serial") // Same-version serialization only 53 public class BasicTextAreaUI extends BasicTextUI { 54 55 /** 56 * Creates a UI for a JTextArea. 57 * 58 * @param ta a text area 59 * @return the UI 60 */ 61 public static ComponentUI createUI(JComponent ta) { 62 return new BasicTextAreaUI(); 63 } 64 65 /** 66 * Constructs a new BasicTextAreaUI object. 67 */ 68 public BasicTextAreaUI() { 69 super(); 70 } 71 72 /** 73 * Fetches the name used as a key to look up properties through the 74 * UIManager. This is used as a prefix to all the standard 75 * text properties. 76 * 77 * @return the name ("TextArea") 78 */ 79 protected String getPropertyPrefix() { 80 return "TextArea"; 81 } 82 83 protected void installDefaults() { 84 super.installDefaults(); 85 //the fix for 4785160 is undone 86 } 87 88 /** 89 * This method gets called when a bound property is changed 90 * on the associated JTextComponent. This is a hook 91 * which UI implementations may change to reflect how the 92 * UI displays bound properties of JTextComponent subclasses. 93 * This is implemented to rebuild the View when the 94 * <em>WrapLine</em> or the <em>WrapStyleWord</em> property changes. 95 * 96 * @param evt the property change event 97 */ 98 protected void propertyChange(PropertyChangeEvent evt) { 99 super.propertyChange(evt); 100 if (evt.getPropertyName().equals("lineWrap") || 101 evt.getPropertyName().equals("wrapStyleWord") || 102 evt.getPropertyName().equals("tabSize")) { 103 // rebuild the view 104 modelChanged(); 105 } else if ("editable".equals(evt.getPropertyName())) { 106 updateFocusTraversalKeys(); 107 } 108 } 109 110 111 /** 112 * The method is overridden to take into account caret width. 113 * 114 * @param c the editor component 115 * @return the preferred size 116 * @throws IllegalArgumentException if invalid value is passed 117 * 118 * @since 1.5 119 */ 120 public Dimension getPreferredSize(JComponent c) { 121 return super.getPreferredSize(c); 122 //the fix for 4785160 is undone 123 } 124 125 /** 126 * The method is overridden to take into account caret width. 127 * 128 * @param c the editor component 129 * @return the minimum size 130 * @throws IllegalArgumentException if invalid value is passed 131 * 132 * @since 1.5 133 */ 134 public Dimension getMinimumSize(JComponent c) { 135 return super.getMinimumSize(c); 136 //the fix for 4785160 is undone 137 } 138 139 /** 140 * Creates the view for an element. Returns a WrappedPlainView or 141 * PlainView. 142 * 143 * @param elem the element 144 * @return the view 145 */ 146 public View create(Element elem) { 147 Document doc = elem.getDocument(); 148 Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/); 149 if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) { 150 // build a view that support bidi 151 return createI18N(elem); 152 } else { 153 JTextComponent c = getComponent(); 154 if (c instanceof JTextArea) { 155 JTextArea area = (JTextArea) c; 156 View v; 157 if (area.getLineWrap()) { 158 v = new WrappedPlainView(elem, area.getWrapStyleWord()); 159 } else { 160 v = new PlainView(elem); 161 } 162 return v; 163 } 164 } 165 return null; 166 } 167 168 View createI18N(Element elem) { 169 String kind = elem.getName(); 170 if (kind != null) { 171 if (kind.equals(AbstractDocument.ContentElementName)) { 172 return new PlainParagraph(elem); 173 } else if (kind.equals(AbstractDocument.ParagraphElementName)) { 174 return new BoxView(elem, View.Y_AXIS); 175 } 176 } 177 return null; 178 } 179 180 /** 181 * Returns the baseline. 182 * 183 * @throws NullPointerException {@inheritDoc} 184 * @throws IllegalArgumentException {@inheritDoc} 185 * @see javax.swing.JComponent#getBaseline(int, int) 186 * @since 1.6 187 */ 188 public int getBaseline(JComponent c, int width, int height) { 189 super.getBaseline(c, width, height); 190 Object i18nFlag = ((JTextComponent)c).getDocument(). 191 getProperty("i18n"); 192 Insets insets = c.getInsets(); 193 if (Boolean.TRUE.equals(i18nFlag)) { 194 View rootView = getRootView((JTextComponent)c); 195 if (rootView.getViewCount() > 0) { 196 height = height - insets.top - insets.bottom; 197 int baseline = insets.top; 198 int fieldBaseline = BasicHTML.getBaseline( 199 rootView.getView(0), width - insets.left - 200 insets.right, height); 201 if (fieldBaseline < 0) { 202 return -1; 203 } 204 return baseline + fieldBaseline; 205 } 206 return -1; 207 } 208 FontMetrics fm = c.getFontMetrics(c.getFont()); 209 return insets.top + fm.getAscent(); 210 } 211 212 /** 213 * Returns an enum indicating how the baseline of the component 214 * changes as the size changes. 215 * 216 * @throws NullPointerException {@inheritDoc} 217 * @see javax.swing.JComponent#getBaseline(int, int) 218 * @since 1.6 219 */ 220 public Component.BaselineResizeBehavior getBaselineResizeBehavior( 221 JComponent c) { 222 super.getBaselineResizeBehavior(c); 223 return Component.BaselineResizeBehavior.CONSTANT_ASCENT; 224 } 225 226 227 /** 228 * Paragraph for representing plain-text lines that support 229 * bidirectional text. 230 */ 231 static class PlainParagraph extends ParagraphView { 232 233 PlainParagraph(Element elem) { 234 super(elem); 235 layoutPool = new LogicalView(elem); 236 layoutPool.setParent(this); 237 } 238 239 public void setParent(View parent) { 240 super.setParent(parent); 241 if (parent != null) { 242 setPropertiesFromAttributes(); 243 } 244 } 245 246 protected void setPropertiesFromAttributes() { 247 Component c = getContainer(); 248 if ((c != null) && (! c.getComponentOrientation().isLeftToRight())) { 249 setJustification(StyleConstants.ALIGN_RIGHT); 250 } else { 251 setJustification(StyleConstants.ALIGN_LEFT); 252 } 253 } 254 255 /** 256 * Fetch the constraining span to flow against for 257 * the given child index. 258 */ 259 public int getFlowSpan(int index) { 260 Component c = getContainer(); 261 if (c instanceof JTextArea) { 262 JTextArea area = (JTextArea) c; 263 if (! area.getLineWrap()) { 264 // no limit if unwrapped 265 return Integer.MAX_VALUE; 266 } 267 } 268 return super.getFlowSpan(index); 269 } 270 271 protected SizeRequirements calculateMinorAxisRequirements(int axis, 272 SizeRequirements r) { 273 SizeRequirements req = super.calculateMinorAxisRequirements(axis, r); 274 Component c = getContainer(); 275 if (c instanceof JTextArea) { 276 JTextArea area = (JTextArea) c; 277 if (! area.getLineWrap()) { 278 // min is pref if unwrapped 279 req.minimum = req.preferred; 280 } else { 281 req.minimum = 0; 282 req.preferred = getWidth(); 283 if (req.preferred == Integer.MAX_VALUE) { 284 // We have been initially set to MAX_VALUE, but we 285 // don't want this as our preferred. 286 req.preferred = 100; 287 } 288 } 289 } 290 return req; 291 } 292 293 /** 294 * Sets the size of the view. If the size has changed, layout 295 * is redone. The size is the full size of the view including 296 * the inset areas. 297 * 298 * @param width the width >= 0 299 * @param height the height >= 0 300 */ 301 public void setSize(float width, float height) { 302 if ((int) width != getWidth()) { 303 preferenceChanged(null, true, true); 304 } 305 super.setSize(width, height); 306 } 307 308 /** 309 * This class can be used to represent a logical view for 310 * a flow. It keeps the children updated to reflect the state 311 * of the model, gives the logical child views access to the 312 * view hierarchy, and calculates a preferred span. It doesn't 313 * do any rendering, layout, or model/view translation. 314 */ 315 static class LogicalView extends CompositeView { 316 317 LogicalView(Element elem) { 318 super(elem); 319 } 320 321 protected int getViewIndexAtPosition(int pos) { 322 Element elem = getElement(); 323 if (elem.getElementCount() > 0) { 324 return elem.getElementIndex(pos); 325 } 326 return 0; 327 } 328 329 protected boolean updateChildren(DocumentEvent.ElementChange ec, 330 DocumentEvent e, ViewFactory f) { 331 return false; 332 } 333 334 protected void loadChildren(ViewFactory f) { 335 Element elem = getElement(); 336 if (elem.getElementCount() > 0) { 337 super.loadChildren(f); 338 } else { 339 View v = new GlyphView(elem); 340 append(v); 341 } 342 } 343 344 public float getPreferredSpan(int axis) { 345 if( getViewCount() != 1 ) 346 throw new Error("One child view is assumed."); 347 348 View v = getView(0); 349 return v.getPreferredSpan(axis); 350 } 351 352 /** 353 * Forward the DocumentEvent to the given child view. This 354 * is implemented to reparent the child to the logical view 355 * (the children may have been parented by a row in the flow 356 * if they fit without breaking) and then execute the superclass 357 * behavior. 358 * 359 * @param v the child view to forward the event to. 360 * @param e the change information from the associated document 361 * @param a the current allocation of the view 362 * @param f the factory to use to rebuild if the view has children 363 * @see #forwardUpdate 364 * @since 1.3 365 */ 366 protected void forwardUpdateToView(View v, DocumentEvent e, 367 Shape a, ViewFactory f) { 368 v.setParent(this); 369 super.forwardUpdateToView(v, e, a, f); 370 } 371 372 // The following methods don't do anything useful, they 373 // simply keep the class from being abstract. 374 375 public void paint(Graphics g, Shape allocation) { 376 } 377 378 protected boolean isBefore(int x, int y, Rectangle alloc) { 379 return false; 380 } 381 382 protected boolean isAfter(int x, int y, Rectangle alloc) { 383 return false; 384 } 385 386 protected View getViewAtPoint(int x, int y, Rectangle alloc) { 387 return null; 388 } 389 390 protected void childAllocation(int index, Rectangle a) { 391 } 392 } 393 } 394 395 }