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&trade;
  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 }