001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import java.awt.event.ActionEvent; 005import java.awt.event.FocusEvent; 006import java.awt.event.FocusListener; 007import java.beans.PropertyChangeListener; 008 009import javax.swing.Action; 010import javax.swing.JPasswordField; 011import javax.swing.TransferHandler; 012import javax.swing.text.Document; 013import javax.swing.text.JTextComponent; 014 015import org.openstreetmap.josm.gui.MainApplication; 016import org.openstreetmap.josm.gui.MapFrame; 017import org.openstreetmap.josm.tools.Logging; 018 019/** 020 * A subclass of {@link JPasswordField} to implement a workaround to 021 * <a href="https://bugs.openjdk.java.net/browse/JDK-6322854">JDK bug 6322854</a>. 022 * 023 * @see <a href="https://josm.openstreetmap.de/ticket/8404">https://josm.openstreetmap.de/ticket/8404</a> 024 * @see <a href="https://hg.netbeans.org/main/rev/33cb2e81b640">https://hg.netbeans.org/main/rev/33cb2e81b640</a> 025 * @since 5752 026 */ 027public class JosmPasswordField extends JPasswordField implements FocusListener { 028 029 /** 030 * Constructs a new <code>JosmPasswordField</code>, 031 * with a default document, <code>null</code> starting 032 * text string, and 0 column width. 033 */ 034 public JosmPasswordField() { 035 workaroundJdkBug6322854(this); 036 addFocusListener(this); 037 } 038 039 /** 040 * Constructs a new <code>JosmPasswordField</code> that uses the 041 * given text storage model and the given number of columns. 042 * This is the constructor through which the other constructors feed. 043 * The echo character is set to '*', but may be changed by the current 044 * Look and Feel. If the document model is 045 * <code>null</code>, a default one will be created. 046 * 047 * @param doc the text storage to use 048 * @param txt the text to be displayed, <code>null</code> if none 049 * @param columns the number of columns to use to calculate 050 * the preferred width >= 0; if columns is set to zero, the 051 * preferred width will be whatever naturally results from 052 * the component implementation 053 */ 054 public JosmPasswordField(Document doc, String txt, int columns) { 055 super(doc, txt, columns); 056 workaroundJdkBug6322854(this); 057 addFocusListener(this); 058 } 059 060 /** 061 * Constructs a new empty <code>JosmPasswordField</code> with the specified 062 * number of columns. A default model is created, and the initial string 063 * is set to <code>null</code>. 064 * 065 * @param columns the number of columns >= 0 066 */ 067 public JosmPasswordField(int columns) { 068 super(columns); 069 workaroundJdkBug6322854(this); 070 addFocusListener(this); 071 } 072 073 /** 074 * Constructs a new <code>JPasswordField</code> initialized with 075 * the specified text and columns. The document model is set to 076 * the default. 077 * 078 * @param text the text to be displayed, <code>null</code> if none 079 * @param columns the number of columns >= 0 080 */ 081 public JosmPasswordField(String text, int columns) { 082 super(text, columns); 083 workaroundJdkBug6322854(this); 084 addFocusListener(this); 085 } 086 087 /** 088 * Constructs a new <code>JosmPasswordField</code> initialized 089 * with the specified text. The document model is set to the 090 * default, and the number of columns to 0. 091 * 092 * @param text the text to be displayed, <code>null</code> if none 093 */ 094 public JosmPasswordField(String text) { 095 super(text); 096 workaroundJdkBug6322854(this); 097 addFocusListener(this); 098 } 099 100 @Override 101 public void focusGained(FocusEvent e) { 102 MapFrame map = MainApplication.getMap(); 103 if (map != null) { 104 map.keyDetector.setEnabled(false); 105 } 106 } 107 108 @Override 109 public void focusLost(FocusEvent e) { 110 MapFrame map = MainApplication.getMap(); 111 if (map != null) { 112 map.keyDetector.setEnabled(true); 113 } 114 } 115 116 /** 117 * Implements a workaround to <a href="https://bugs.openjdk.java.net/browse/JDK-6322854">JDK bug 6322854</a>. 118 * This method can be deleted after Oracle decides to fix this bug... 119 * @param text The {@link JTextComponent} to protect. 120 */ 121 public static final void workaroundJdkBug6322854(final JTextComponent text) { 122 if (text != null) { 123 text.getActionMap().put("paste", new Action() { 124 125 private final Action pasteAction = TransferHandler.getPasteAction(); 126 127 @Override 128 public void actionPerformed(ActionEvent e) { 129 try { 130 pasteAction.actionPerformed(e); 131 } catch (NullPointerException npe) { // NOPMD 132 Logging.log(Logging.LEVEL_ERROR, "NullPointerException occured because of JDK bug 6322854. " 133 +"Copy/Paste operation has not been performed. Please complain to Oracle: "+ 134 "https://bugs.openjdk.java.net/browse/JDK-6322854", npe); 135 } 136 } 137 138 @Override 139 public void setEnabled(boolean b) { 140 pasteAction.setEnabled(b); 141 } 142 143 @Override 144 public void removePropertyChangeListener(PropertyChangeListener listener) { 145 pasteAction.removePropertyChangeListener(listener); 146 } 147 148 @Override 149 public void putValue(String key, Object value) { 150 pasteAction.putValue(key, value); 151 } 152 153 @Override 154 public boolean isEnabled() { 155 return pasteAction.isEnabled(); 156 } 157 158 @Override 159 public Object getValue(String key) { 160 return pasteAction.getValue(key); 161 } 162 163 @Override 164 public void addPropertyChangeListener(PropertyChangeListener listener) { 165 pasteAction.addPropertyChangeListener(listener); 166 } 167 }); 168 } 169 } 170}