1 /*
   2  * Copyright (c) 1997, 2015, 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.awt.*;
  28 import java.awt.event.KeyEvent;
  29 import java.awt.event.FocusEvent;
  30 import java.awt.event.InputEvent;
  31 import java.beans.PropertyChangeEvent;
  32 import java.io.Reader;
  33 import javax.swing.*;
  34 import javax.swing.border.*;
  35 import javax.swing.event.*;
  36 import javax.swing.text.*;
  37 import javax.swing.plaf.*;
  38 import sun.swing.DefaultLookup;
  39 
  40 /**
  41  * Basis of a look and feel for a JTextField.
  42  * <p>
  43  * <strong>Warning:</strong>
  44  * Serialized objects of this class will not be compatible with
  45  * future Swing releases. The current serialization support is
  46  * appropriate for short term storage or RMI between applications running
  47  * the same version of Swing.  As of 1.4, support for long term storage
  48  * of all JavaBeans
  49  * has been added to the <code>java.beans</code> package.
  50  * Please see {@link java.beans.XMLEncoder}.
  51  *
  52  * @author  Timothy Prinzing
  53  */
  54 @SuppressWarnings("serial") // Same-version serialization only
  55 public class BasicTextFieldUI extends BasicTextUI {
  56 
  57     /**
  58      * Creates a UI for a JTextField.
  59      *
  60      * @param c the text field
  61      * @return the UI
  62      */
  63     public static ComponentUI createUI(JComponent c) {
  64         return new BasicTextFieldUI();
  65     }
  66 
  67     /**
  68      * Creates a new BasicTextFieldUI.
  69      */
  70     public BasicTextFieldUI() {
  71         super();
  72     }
  73 
  74     /**
  75      * Fetches the name used as a key to lookup properties through the
  76      * UIManager.  This is used as a prefix to all the standard
  77      * text properties.
  78      *
  79      * @return the name ("TextField")
  80      */
  81     protected String getPropertyPrefix() {
  82         return "TextField";
  83     }
  84 
  85     /**
  86      * Creates a view (FieldView) based on an element.
  87      *
  88      * @param elem the element
  89      * @return the view
  90      */
  91     public View create(Element elem) {
  92         Document doc = elem.getDocument();
  93         Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
  94         if (Boolean.TRUE.equals(i18nFlag)) {
  95             // To support bidirectional text, we build a more heavyweight
  96             // representation of the field.
  97             String kind = elem.getName();
  98             if (kind != null) {
  99                 if (kind.equals(AbstractDocument.ContentElementName)) {
 100                     return new GlyphView(elem){
 101                         @Override
 102                         public float getMinimumSpan(int axis) {
 103                             // no wrap
 104                             return getPreferredSpan(axis);
 105                         }
 106                     };
 107                 } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
 108                     return new I18nFieldView(elem);
 109                 }
 110             }
 111             // this shouldn't happen, should probably throw in this case.
 112         }
 113         return new FieldView(elem);
 114     }
 115 
 116     /**
 117      * Returns the baseline.
 118      *
 119      * @throws NullPointerException {@inheritDoc}
 120      * @throws IllegalArgumentException {@inheritDoc}
 121      * @see javax.swing.JComponent#getBaseline(int, int)
 122      * @since 1.6
 123      */
 124     public int getBaseline(JComponent c, int width, int height) {
 125         super.getBaseline(c, width, height);
 126         View rootView = getRootView((JTextComponent)c);
 127         if (rootView.getViewCount() > 0) {
 128             Insets insets = c.getInsets();
 129             height = height - insets.top - insets.bottom;
 130             if (height > 0) {
 131                 int baseline = insets.top;
 132                 View fieldView = rootView.getView(0);
 133                 int vspan = (int)fieldView.getPreferredSpan(View.Y_AXIS);
 134                 if (height != vspan) {
 135                     int slop = height - vspan;
 136                     baseline += slop / 2;
 137                 }
 138                 if (fieldView instanceof I18nFieldView) {
 139                     int fieldBaseline = BasicHTML.getBaseline(
 140                             fieldView, width - insets.left - insets.right,
 141                             height);
 142                     if (fieldBaseline < 0) {
 143                         return -1;
 144                     }
 145                     baseline += fieldBaseline;
 146                 }
 147                 else {
 148                     FontMetrics fm = c.getFontMetrics(c.getFont());
 149                     baseline += fm.getAscent();
 150                 }
 151                 return baseline;
 152             }
 153         }
 154         return -1;
 155     }
 156 
 157     /**
 158      * Returns an enum indicating how the baseline of the component
 159      * changes as the size changes.
 160      *
 161      * @throws NullPointerException {@inheritDoc}
 162      * @see javax.swing.JComponent#getBaseline(int, int)
 163      * @since 1.6
 164      */
 165     public Component.BaselineResizeBehavior getBaselineResizeBehavior(
 166             JComponent c) {
 167         super.getBaselineResizeBehavior(c);
 168         return Component.BaselineResizeBehavior.CENTER_OFFSET;
 169     }
 170 
 171 
 172     /**
 173      * A field view that support bidirectional text via the
 174      * support provided by ParagraphView.
 175      */
 176     static class I18nFieldView extends ParagraphView {
 177 
 178         I18nFieldView(Element elem) {
 179             super(elem);
 180         }
 181 
 182         /**
 183          * Fetch the constraining span to flow against for
 184          * the given child index.  There is no limit for
 185          * a field since it scrolls, so this is implemented to
 186          * return <code>Integer.MAX_VALUE</code>.
 187          */
 188         public int getFlowSpan(int index) {
 189             return Integer.MAX_VALUE;
 190         }
 191 
 192         protected void setJustification(int j) {
 193             // Justification is done in adjustAllocation(), so disable
 194             // ParagraphView's justification handling by doing nothing here.
 195         }
 196 
 197         static boolean isLeftToRight( java.awt.Component c ) {
 198             return c.getComponentOrientation().isLeftToRight();
 199         }
 200 
 201         /**
 202          * Adjusts the allocation given to the view
 203          * to be a suitable allocation for a text field.
 204          * If the view has been allocated more than the
 205          * preferred span vertically, the allocation is
 206          * changed to be centered vertically.  Horizontally
 207          * the view is adjusted according to the horizontal
 208          * alignment property set on the associated JTextField
 209          * (if that is the type of the hosting component).
 210          *
 211          * @param a the allocation given to the view, which may need
 212          *  to be adjusted.
 213          * @return the allocation that the superclass should use.
 214          */
 215         Shape adjustAllocation(Shape a) {
 216             if (a != null) {
 217                 Rectangle bounds = a.getBounds();
 218                 int vspan = (int) getPreferredSpan(Y_AXIS);
 219                 int hspan = (int) getPreferredSpan(X_AXIS);
 220                 if (bounds.height != vspan) {
 221                     int slop = bounds.height - vspan;
 222                     bounds.y += slop / 2;
 223                     bounds.height -= slop;
 224                 }
 225 
 226                 // horizontal adjustments
 227                 Component c = getContainer();
 228                 if (c instanceof JTextField) {
 229                     JTextField field = (JTextField) c;
 230                     BoundedRangeModel vis = field.getHorizontalVisibility();
 231                     int max = Math.max(hspan, bounds.width);
 232                     int value = vis.getValue();
 233                     int extent = Math.min(max, bounds.width - 1);
 234                     if ((value + extent) > max) {
 235                         value = max - extent;
 236                     }
 237                     vis.setRangeProperties(value, extent, vis.getMinimum(),
 238                                            max, false);
 239                     if (hspan < bounds.width) {
 240                         // horizontally align the interior
 241                         int slop = bounds.width - 1 - hspan;
 242 
 243                         int align = ((JTextField)c).getHorizontalAlignment();
 244                         if(isLeftToRight(c)) {
 245                             if(align==LEADING) {
 246                                 align = LEFT;
 247                             }
 248                             else if(align==TRAILING) {
 249                                 align = RIGHT;
 250                             }
 251                         }
 252                         else {
 253                             if(align==LEADING) {
 254                                 align = RIGHT;
 255                             }
 256                             else if(align==TRAILING) {
 257                                 align = LEFT;
 258                             }
 259                         }
 260 
 261                         switch (align) {
 262                         case SwingConstants.CENTER:
 263                             bounds.x += slop / 2;
 264                             bounds.width -= slop;
 265                             break;
 266                         case SwingConstants.RIGHT:
 267                             bounds.x += slop;
 268                             bounds.width -= slop;
 269                             break;
 270                         }
 271                     } else {
 272                         // adjust the allocation to match the bounded range.
 273                         bounds.width = hspan;
 274                         bounds.x -= vis.getValue();
 275                     }
 276                 }
 277                 return bounds;
 278             }
 279             return null;
 280         }
 281 
 282         /**
 283          * Update the visibility model with the associated JTextField
 284          * (if there is one) to reflect the current visibility as a
 285          * result of changes to the document model.  The bounded
 286          * range properties are updated.  If the view hasn't yet been
 287          * shown the extent will be zero and we just set it to be full
 288          * until determined otherwise.
 289          */
 290         void updateVisibilityModel() {
 291             Component c = getContainer();
 292             if (c instanceof JTextField) {
 293                 JTextField field = (JTextField) c;
 294                 BoundedRangeModel vis = field.getHorizontalVisibility();
 295                 int hspan = (int) getPreferredSpan(X_AXIS);
 296                 int extent = vis.getExtent();
 297                 int maximum = Math.max(hspan, extent);
 298                 extent = (extent == 0) ? maximum : extent;
 299                 int value = maximum - extent;
 300                 int oldValue = vis.getValue();
 301                 if ((oldValue + extent) > maximum) {
 302                     oldValue = maximum - extent;
 303                 }
 304                 value = Math.max(0, Math.min(value, oldValue));
 305                 vis.setRangeProperties(value, extent, 0, maximum, false);
 306             }
 307         }
 308 
 309         // --- View methods -------------------------------------------
 310 
 311         /**
 312          * Renders using the given rendering surface and area on that surface.
 313          * The view may need to do layout and create child views to enable
 314          * itself to render into the given allocation.
 315          *
 316          * @param g the rendering surface to use
 317          * @param a the allocated region to render into
 318          *
 319          * @see View#paint
 320          */
 321         public void paint(Graphics g, Shape a) {
 322             Rectangle r = (Rectangle) a;
 323             g.clipRect(r.x, r.y, r.width, r.height);
 324             super.paint(g, adjustAllocation(a));
 325         }
 326 
 327         /**
 328          * Determines the resizability of the view along the
 329          * given axis.  A value of 0 or less is not resizable.
 330          *
 331          * @param axis View.X_AXIS or View.Y_AXIS
 332          * @return the weight -> 1 for View.X_AXIS, else 0
 333          */
 334         public int getResizeWeight(int axis) {
 335             if (axis == View.X_AXIS) {
 336                 return 1;
 337             }
 338             return 0;
 339         }
 340 
 341         /**
 342          * Provides a mapping from the document model coordinate space
 343          * to the coordinate space of the view mapped to it.
 344          *
 345          * @param pos the position to convert >= 0
 346          * @param a the allocated region to render into
 347          * @return the bounding box of the given position
 348          * @exception BadLocationException  if the given position does not
 349          *   represent a valid location in the associated document
 350          * @see View#modelToView
 351          */
 352         public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
 353             return super.modelToView(pos, adjustAllocation(a), b);
 354         }
 355 
 356         /**
 357          * Provides a mapping from the document model coordinate space
 358          * to the coordinate space of the view mapped to it.
 359          *
 360          * @param p0 the position to convert >= 0
 361          * @param b0 the bias toward the previous character or the
 362          *  next character represented by p0, in case the
 363          *  position is a boundary of two views.
 364          * @param p1 the position to convert >= 0
 365          * @param b1 the bias toward the previous character or the
 366          *  next character represented by p1, in case the
 367          *  position is a boundary of two views.
 368          * @param a the allocated region to render into
 369          * @return the bounding box of the given position is returned
 370          * @exception BadLocationException  if the given position does
 371          *   not represent a valid location in the associated document
 372          * @exception IllegalArgumentException for an invalid bias argument
 373          * @see View#viewToModel
 374          */
 375         public Shape modelToView(int p0, Position.Bias b0,
 376                                  int p1, Position.Bias b1, Shape a)
 377             throws BadLocationException
 378         {
 379             return super.modelToView(p0, b0, p1, b1, adjustAllocation(a));
 380         }
 381 
 382         /**
 383          * Provides a mapping from the view coordinate space to the logical
 384          * coordinate space of the model.
 385          *
 386          * @param fx the X coordinate >= 0.0f
 387          * @param fy the Y coordinate >= 0.0f
 388          * @param a the allocated region to render into
 389          * @return the location within the model that best represents the
 390          *  given point in the view
 391          * @see View#viewToModel
 392          */
 393         public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
 394             return super.viewToModel(fx, fy, adjustAllocation(a), bias);
 395         }
 396 
 397         /**
 398          * Gives notification that something was inserted into the document
 399          * in a location that this view is responsible for.
 400          *
 401          * @param changes the change information from the associated document
 402          * @param a the current allocation of the view
 403          * @param f the factory to use to rebuild if the view has children
 404          * @see View#insertUpdate
 405          */
 406         public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
 407             super.insertUpdate(changes, adjustAllocation(a), f);
 408             updateVisibilityModel();
 409         }
 410 
 411         /**
 412          * Gives notification that something was removed from the document
 413          * in a location that this view is responsible for.
 414          *
 415          * @param changes the change information from the associated document
 416          * @param a the current allocation of the view
 417          * @param f the factory to use to rebuild if the view has children
 418          * @see View#removeUpdate
 419          */
 420         public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
 421             super.removeUpdate(changes, adjustAllocation(a), f);
 422             updateVisibilityModel();
 423         }
 424 
 425     }
 426 
 427 }