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 sun.swing.SwingUtilities2;
28 import java.awt.*;
29 import java.awt.font.FontRenderContext;
30 import javax.swing.JPasswordField;
31 
32 /**
33  * Implements a View suitable for use in JPasswordField
34  * UI implementations.  This is basically a field ui that
35  * renders its contents as the echo character specified
36  * in the associated component (if it can narrow the
37  * component to a JPasswordField).
38  *
39  * @author  Timothy Prinzing
40  * @see     View
41  */
42 public class PasswordView extends FieldView {
43 
44     /**
45      * Constructs a new view wrapped on an element.
46      *
47      * @param elem the element
48      */
PasswordView(Element elem)49     public PasswordView(Element elem) {
50         super(elem);
51     }
52 
53     /**
54      * Renders the given range in the model as normal unselected
55      * text.  This sets the foreground color and echos the characters
56      * using the value returned by getEchoChar().
57      *
58      * @param g the graphics context
59      * @param x the starting X coordinate >= 0
60      * @param y the starting Y coordinate >= 0
61      * @param p0 the starting offset in the model >= 0
62      * @param p1 the ending offset in the model >= p0
63      * @return the X location of the end of the range >= 0
64      * @exception BadLocationException if p0 or p1 are out of range
65      *
66      * @deprecated replaced by
67      *     {@link #drawUnselectedText(Graphics2D, float, float, int, int)}
68      */
69     @Deprecated(since = "9")
70     @Override
drawUnselectedText(Graphics g, int x, int y, int p0, int p1)71     protected int drawUnselectedText(Graphics g, int x, int y,
72                                      int p0, int p1) throws BadLocationException {
73         return (int) drawUnselectedTextImpl(g, x, y, p0, p1, false);
74     }
75 
76     @Override
drawUnselectedText(Graphics2D g, float x, float y, int p0, int p1)77     protected float drawUnselectedText(Graphics2D g, float x, float y,
78                                        int p0, int p1)
79             throws BadLocationException
80     {
81         return drawUnselectedTextImpl(g, x, y, p0, p1, true);
82     }
83 
84     @SuppressWarnings("deprecation")
drawUnselectedTextImpl(Graphics g, float x, float y, int p0, int p1, boolean useFPAPI)85     private float drawUnselectedTextImpl(Graphics g, float x, float y,
86                                          int p0, int p1,
87                                          boolean useFPAPI)
88             throws BadLocationException
89     {
90         Container c = getContainer();
91         if (c instanceof JPasswordField) {
92             JPasswordField f = (JPasswordField) c;
93             if (!f.echoCharIsSet()) {
94                 boolean useDrawUnselectedFPAPI = useFPAPI
95                         && drawUnselectedTextOverridden
96                         && g instanceof Graphics2D;
97                 return (useDrawUnselectedFPAPI )
98                         ? super.drawUnselectedText((Graphics2D) g, x, y, p0, p1)
99                         : super.drawUnselectedText(g, (int) x, (int) y, p0, p1);
100             }
101             if (f.isEnabled()) {
102                 g.setColor(f.getForeground());
103             }
104             else {
105                 g.setColor(f.getDisabledTextColor());
106             }
107             char echoChar = f.getEchoChar();
108             int n = p1 - p0;
109             boolean useEchoCharFPAPI = useFPAPI
110                     && drawEchoCharacterOverridden
111                     && g instanceof Graphics2D;
112             for (int i = 0; i < n; i++) {
113                 x = (useEchoCharFPAPI)
114                         ? drawEchoCharacter((Graphics2D) g, x, y, echoChar)
115                         : drawEchoCharacter(g, (int) x, (int) y, echoChar);
116             }
117         }
118         return x;
119     }
120 
121     /**
122      * Renders the given range in the model as selected text.  This
123      * is implemented to render the text in the color specified in
124      * the hosting component.  It assumes the highlighter will render
125      * the selected background.  Uses the result of getEchoChar() to
126      * display the characters.
127      *
128      * @param g the graphics context
129      * @param x the starting X coordinate &gt;= 0
130      * @param y the starting Y coordinate &gt;= 0
131      * @param p0 the starting offset in the model &gt;= 0
132      * @param p1 the ending offset in the model &gt;= p0
133      * @return the X location of the end of the range &gt;= 0
134      * @exception BadLocationException if p0 or p1 are out of range
135      *
136      * @deprecated replaced by
137      *     {@link #drawSelectedText(Graphics2D, float, float, int, int)}
138      */
139     @Deprecated(since = "9")
140     @Override
drawSelectedText(Graphics g, int x, int y, int p0, int p1)141     protected int drawSelectedText(Graphics g, int x,
142                                    int y, int p0, int p1) throws BadLocationException {
143         return (int) drawSelectedTextImpl(g, x, y, p0, p1, false);
144     }
145 
146     @Override
drawSelectedText(Graphics2D g, float x, float y, int p0, int p1)147     protected float drawSelectedText(Graphics2D g, float x, float y,
148                                      int p0, int p1) throws BadLocationException
149     {
150         return drawSelectedTextImpl(g, x, y, p0, p1, true);
151     }
152 
153     @SuppressWarnings("deprecation")
drawSelectedTextImpl(Graphics g, float x, float y, int p0, int p1, boolean useFPAPI)154     private float drawSelectedTextImpl(Graphics g, float x, float y,
155                                        int p0, int p1,
156                                        boolean useFPAPI)
157             throws BadLocationException {
158         g.setColor(selected);
159         Container c = getContainer();
160         if (c instanceof JPasswordField) {
161             JPasswordField f = (JPasswordField) c;
162             if (!f.echoCharIsSet()) {
163                 boolean useDrawUnselectedFPAPI = useFPAPI
164                         && drawSelectedTextOverridden
165                         && g instanceof Graphics2D;
166                 return (useFPAPI)
167                         ? super.drawSelectedText((Graphics2D) g, x, y, p0, p1)
168                         : super.drawSelectedText(g, (int) x, (int) y, p0, p1);
169             }
170             char echoChar = f.getEchoChar();
171             int n = p1 - p0;
172             boolean useEchoCharFPAPI = useFPAPI
173                     && drawEchoCharacterOverridden
174                     && g instanceof Graphics2D;
175             for (int i = 0; i < n; i++) {
176                 x = (useEchoCharFPAPI)
177                         ? drawEchoCharacter((Graphics2D) g, x, y, echoChar)
178                         : drawEchoCharacter(g, (int) x, (int) y, echoChar);
179 
180             }
181         }
182         return x;
183     }
184 
185     /**
186      * Renders the echo character, or whatever graphic should be used
187      * to display the password characters.  The color in the Graphics
188      * object is set to the appropriate foreground color for selected
189      * or unselected text.
190      *
191      * @param g the graphics context
192      * @param x the starting X coordinate &gt;= 0
193      * @param y the starting Y coordinate &gt;= 0
194      * @param c the echo character
195      * @return the updated X position &gt;= 0
196      *
197      * @deprecated replaced by
198      *     {@link #drawEchoCharacter(Graphics2D, float, float, char)}
199      */
200     @Deprecated(since = "9")
drawEchoCharacter(Graphics g, int x, int y, char c)201     protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
202         return (int) drawEchoCharacterImpl(g, x, y, c, false);
203     }
204 
205     /**
206      * Renders the echo character, or whatever graphic should be used
207      * to display the password characters.  The color in the Graphics
208      * object is set to the appropriate foreground color for selected
209      * or unselected text.
210      *
211      * @param g the graphics context
212      * @param x the starting X coordinate {@code >= 0}
213      * @param y the starting Y coordinate {@code >= 0}
214      * @param c the echo character
215      * @return the updated X position {@code >= 0}
216      *
217      * @since 9
218      */
drawEchoCharacter(Graphics2D g, float x, float y, char c)219     protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
220         return drawEchoCharacterImpl(g, x, y, c, true);
221     }
222 
drawEchoCharacterImpl(Graphics g, float x, float y, char c, boolean useFPAPI)223     private float drawEchoCharacterImpl(Graphics g, float x, float y,
224                                         char c, boolean useFPAPI) {
225         ONE[0] = c;
226         SwingUtilities2.drawChars(Utilities.getJComponent(this),
227                                   g, ONE, 0, 1, x, y);
228         if (useFPAPI) {
229             return x + g.getFontMetrics().charWidth(c);
230         } else {
231             FontRenderContext frc = g.getFontMetrics().getFontRenderContext();
232             return x + (float) g.getFont().getStringBounds(ONE, 0, 1, frc).getWidth();
233         }
234     }
235 
236     /**
237      * Provides a mapping from the document model coordinate space
238      * to the coordinate space of the view mapped to it.
239      *
240      * @param pos the position to convert &gt;= 0
241      * @param a the allocated region to render into
242      * @return the bounding box of the given position
243      * @exception BadLocationException  if the given position does not
244      *   represent a valid location in the associated document
245      * @see View#modelToView
246      */
modelToView(int pos, Shape a, Position.Bias b)247     public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
248         Container c = getContainer();
249         if (c instanceof JPasswordField) {
250             JPasswordField f = (JPasswordField) c;
251             if (! f.echoCharIsSet()) {
252                 return super.modelToView(pos, a, b);
253             }
254             char echoChar = f.getEchoChar();
255             FontMetrics m = f.getFontMetrics(f.getFont());
256 
257             Rectangle alloc = adjustAllocation(a).getBounds();
258             int dx = (pos - getStartOffset()) * m.charWidth(echoChar);
259             alloc.x += dx;
260             alloc.width = 1;
261             return alloc;
262         }
263         return null;
264     }
265 
266     /**
267      * Provides a mapping from the view coordinate space to the logical
268      * coordinate space of the model.
269      *
270      * @param fx the X coordinate &gt;= 0.0f
271      * @param fy the Y coordinate &gt;= 0.0f
272      * @param a the allocated region to render into
273      * @return the location within the model that best represents the
274      *  given point in the view
275      * @see View#viewToModel
276      */
viewToModel(float fx, float fy, Shape a, Position.Bias[] bias)277     public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
278         bias[0] = Position.Bias.Forward;
279         int n = 0;
280         Container c = getContainer();
281         if (c instanceof JPasswordField) {
282             JPasswordField f = (JPasswordField) c;
283             if (! f.echoCharIsSet()) {
284                 return super.viewToModel(fx, fy, a, bias);
285             }
286             char echoChar = f.getEchoChar();
287             int charWidth = f.getFontMetrics(f.getFont()).charWidth(echoChar);
288             a = adjustAllocation(a);
289             Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
290                               a.getBounds();
291             n = (charWidth > 0 ?
292                  ((int)fx - alloc.x) / charWidth : Integer.MAX_VALUE);
293             if (n < 0) {
294                 n = 0;
295             }
296             else if (n > (getStartOffset() + getDocument().getLength())) {
297                 n = getDocument().getLength() - getStartOffset();
298             }
299         }
300         return getStartOffset() + n;
301     }
302 
303     /**
304      * Determines the preferred span for this view along an
305      * axis.
306      *
307      * @param axis may be either View.X_AXIS or View.Y_AXIS
308      * @return   the span the view would like to be rendered into &gt;= 0.
309      *           Typically the view is told to render into the span
310      *           that is returned, although there is no guarantee.
311      *           The parent may choose to resize or break the view.
312      */
getPreferredSpan(int axis)313     public float getPreferredSpan(int axis) {
314         switch (axis) {
315         case View.X_AXIS:
316             Container c = getContainer();
317             if (c instanceof JPasswordField) {
318                 JPasswordField f = (JPasswordField) c;
319                 if (f.echoCharIsSet()) {
320                     char echoChar = f.getEchoChar();
321                     FontMetrics m = f.getFontMetrics(f.getFont());
322                     Document doc = getDocument();
323                     return m.charWidth(echoChar) * getDocument().getLength();
324                 }
325             }
326         }
327         return super.getPreferredSpan(axis);
328     }
329 
330     static char[] ONE = new char[1];
331 
332     private final boolean drawEchoCharacterOverridden =
333             getFPMethodOverridden(getClass(), "drawEchoCharacter", FPMethodArgs.GNNC);
334 }
335