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