source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java@ 14463

Last change on this file since 14463 was 14463, checked in by Don-vip, 5 years ago

fix #17040 - fix memory leaks when calling history dialog

  • Property svn:eol-style set to native
File size: 7.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.widgets;
3
4import java.awt.Color;
5import java.awt.FontMetrics;
6import java.awt.Graphics;
7import java.awt.Graphics2D;
8import java.awt.Insets;
9import java.awt.RenderingHints;
10import java.awt.event.FocusEvent;
11import java.awt.event.FocusListener;
12
13import javax.swing.JTextField;
14import javax.swing.text.Document;
15
16import org.openstreetmap.josm.gui.MainApplication;
17import org.openstreetmap.josm.gui.MapFrame;
18import org.openstreetmap.josm.tools.Destroyable;
19
20/**
21 * Subclass of {@link JTextField} that:<ul>
22 * <li>adds a "native" context menu (undo/redo/cut/copy/paste/select all)</li>
23 * <li>adds an optional "hint" displayed when no text has been entered</li>
24 * <li>disables the global advanced key press detector when focused</li>
25 * <li>implements a workaround to <a href="https://bugs.openjdk.java.net/browse/JDK-6322854">JDK bug 6322854</a></li>
26 * </ul><br>This class must be used everywhere in core and plugins instead of {@code JTextField}.
27 * @since 5886
28 */
29public class JosmTextField extends JTextField implements Destroyable, FocusListener {
30
31 private final PopupMenuLauncher launcher;
32 private String hint;
33
34 /**
35 * Constructs a new <code>JosmTextField</code> that uses the given text
36 * storage model and the given number of columns.
37 * This is the constructor through which the other constructors feed.
38 * If the document is <code>null</code>, a default model is created.
39 *
40 * @param doc the text storage to use; if this is <code>null</code>,
41 * a default will be provided by calling the
42 * <code>createDefaultModel</code> method
43 * @param text the initial string to display, or <code>null</code>
44 * @param columns the number of columns to use to calculate
45 * the preferred width &gt;= 0; if <code>columns</code>
46 * is set to zero, the preferred width will be whatever
47 * naturally results from the component implementation
48 * @throws IllegalArgumentException if <code>columns</code> &lt; 0
49 */
50 public JosmTextField(Document doc, String text, int columns) {
51 this(doc, text, columns, true);
52 }
53
54 /**
55 * Constructs a new <code>JosmTextField</code> that uses the given text
56 * storage model and the given number of columns.
57 * This is the constructor through which the other constructors feed.
58 * If the document is <code>null</code>, a default model is created.
59 *
60 * @param doc the text storage to use; if this is <code>null</code>,
61 * a default will be provided by calling the
62 * <code>createDefaultModel</code> method
63 * @param text the initial string to display, or <code>null</code>
64 * @param columns the number of columns to use to calculate
65 * the preferred width &gt;= 0; if <code>columns</code>
66 * is set to zero, the preferred width will be whatever
67 * naturally results from the component implementation
68 * @param undoRedo Enables or not Undo/Redo feature. Not recommended for table cell editors, unless each cell provides its own editor
69 * @throws IllegalArgumentException if <code>columns</code> &lt; 0
70 */
71 public JosmTextField(Document doc, String text, int columns, boolean undoRedo) {
72 super(doc, text, columns);
73 launcher = TextContextualPopupMenu.enableMenuFor(this, undoRedo);
74 // Fix minimum size when columns are specified
75 if (columns > 0) {
76 setMinimumSize(getPreferredSize());
77 }
78 addFocusListener(this);
79 // Workaround for Java bug 6322854
80 JosmPasswordField.workaroundJdkBug6322854(this);
81 }
82
83 /**
84 * Constructs a new <code>JosmTextField</code> initialized with the
85 * specified text and columns. A default model is created.
86 *
87 * @param text the text to be displayed, or <code>null</code>
88 * @param columns the number of columns to use to calculate
89 * the preferred width; if columns is set to zero, the
90 * preferred width will be whatever naturally results from
91 * the component implementation
92 */
93 public JosmTextField(String text, int columns) {
94 this(null, text, columns);
95 }
96
97 /**
98 * Constructs a new <code>JosmTextField</code> initialized with the
99 * specified text. A default model is created and the number of
100 * columns is 0.
101 *
102 * @param text the text to be displayed, or <code>null</code>
103 */
104 public JosmTextField(String text) {
105 this(null, text, 0);
106 }
107
108 /**
109 * Constructs a new empty <code>JosmTextField</code> with the specified
110 * number of columns.
111 * A default model is created and the initial string is set to
112 * <code>null</code>.
113 *
114 * @param columns the number of columns to use to calculate
115 * the preferred width; if columns is set to zero, the
116 * preferred width will be whatever naturally results from
117 * the component implementation
118 */
119 public JosmTextField(int columns) {
120 this(null, null, columns);
121 }
122
123 /**
124 * Constructs a new <code>JosmTextField</code>. A default model is created,
125 * the initial string is <code>null</code>,
126 * and the number of columns is set to 0.
127 */
128 public JosmTextField() {
129 this(null, null, 0);
130 }
131
132 /**
133 * Replies the hint displayed when no text has been entered.
134 * @return the hint
135 * @since 7505
136 */
137 public final String getHint() {
138 return hint;
139 }
140
141 /**
142 * Sets the hint to display when no text has been entered.
143 * @param hint the hint to set
144 * @since 7505
145 */
146 public final void setHint(String hint) {
147 this.hint = hint;
148 }
149
150 @Override
151 public void paint(Graphics g) {
152 super.paint(g);
153 if (hint != null && !hint.isEmpty() && getText().isEmpty() && !isFocusOwner()) {
154 // Taken from http://stackoverflow.com/a/24571681/2257172
155 int h = getHeight();
156 if (g instanceof Graphics2D) {
157 ((Graphics2D) g).setRenderingHint(
158 RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
159 }
160 Insets ins = getInsets();
161 FontMetrics fm = g.getFontMetrics();
162 int c0 = getBackground().getRGB();
163 int c1 = getForeground().getRGB();
164 int m = 0xfefefefe;
165 int c2 = ((c0 & m) >>> 1) + ((c1 & m) >>> 1);
166 g.setColor(new Color(c2, true));
167 g.drawString(hint, ins.left, h / 2 + fm.getAscent() / 2 - 2);
168 }
169 }
170
171 @Override
172 public void focusGained(FocusEvent e) {
173 MapFrame map = MainApplication.getMap();
174 if (map != null) {
175 map.keyDetector.setEnabled(false);
176 }
177 repaint();
178 }
179
180 @Override
181 public void focusLost(FocusEvent e) {
182 MapFrame map = MainApplication.getMap();
183 if (map != null) {
184 map.keyDetector.setEnabled(true);
185 }
186 repaint();
187 }
188
189 @Override
190 public void destroy() {
191 removeFocusListener(this);
192 TextContextualPopupMenu.disableMenuFor(this, launcher);
193 }
194}
Note: See TracBrowser for help on using the repository browser.