1 /* 2 * Copyright (c) 2011, 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 26 27 package sun.lwawt; 28 29 import java.awt.Dimension; 30 import java.awt.FontMetrics; 31 import java.awt.Insets; 32 import java.awt.SystemColor; 33 import java.awt.TextComponent; 34 import java.awt.event.FocusEvent; 35 import java.awt.event.TextEvent; 36 import java.awt.event.InputMethodListener; 37 import java.awt.event.InputMethodEvent; 38 import java.awt.im.InputMethodRequests; 39 import java.awt.peer.TextComponentPeer; 40 import sun.awt.AWTAccessor; 41 42 import javax.swing.JComponent; 43 import javax.swing.event.DocumentEvent; 44 import javax.swing.event.DocumentListener; 45 import javax.swing.text.Document; 46 import javax.swing.text.JTextComponent; 47 48 abstract class LWTextComponentPeer<T extends TextComponent, D extends JComponent> 49 extends LWComponentPeer<T, D> 50 implements DocumentListener, TextComponentPeer, InputMethodListener { 51 52 /** 53 * Character with reasonable value between the minimum width and maximum. 54 */ 55 protected static final char WIDE_CHAR = 'w'; 56 private volatile boolean firstChangeSkipped; 57 58 LWTextComponentPeer(final T target, 59 final PlatformComponent platformComponent) { 60 super(target, platformComponent); 61 if (!getTarget().isBackgroundSet()) { 62 getTarget().setBackground(SystemColor.text); 63 } 64 } 65 66 @Override 67 public void initialize() { 68 super.initialize(); 69 synchronized (getDelegateLock()) { 70 // This listener should be added before setText(). 71 getTextComponent().getDocument().addDocumentListener(this); 72 } 73 setEditable(getTarget().isEditable()); 74 setText(getTarget().getText()); 75 getTarget().addInputMethodListener(this); 76 final int start = getTarget().getSelectionStart(); 77 final int end = getTarget().getSelectionEnd(); 78 if (end > start) { 79 select(start, end); 80 } 81 setCaretPosition(getTarget().getCaretPosition()); 82 firstChangeSkipped = true; 83 } 84 85 abstract JTextComponent getTextComponent(); 86 87 public Dimension getPreferredSize(final int rows, final int columns) { 88 final Insets insets; 89 synchronized (getDelegateLock()) { 90 insets = getDelegate().getInsets(); 91 } 92 final int borderHeight = insets.top + insets.bottom; 93 final int borderWidth = insets.left + insets.right; 94 final FontMetrics fm = getFontMetrics(getFont()); 95 final int charWidth = (fm != null) ? fm.charWidth(WIDE_CHAR) : 10; 96 final int itemHeight = (fm != null) ? fm.getHeight() : 10; 97 return new Dimension(columns * charWidth + borderWidth, 98 rows * itemHeight + borderHeight); 99 } 100 101 @Override 102 public final void setEditable(final boolean editable) { 103 synchronized (getDelegateLock()) { 104 getTextComponent().setEditable(editable); 105 } 106 } 107 108 @Override 109 public final String getText() { 110 synchronized (getDelegateLock()) { 111 return getTextComponent().getText(); 112 } 113 } 114 115 @Override 116 public void setText(final String l) { 117 synchronized (getDelegateLock()) { 118 // JTextArea.setText() posts two different events (remove & insert). 119 // Since we make no differences between text events, 120 // the document listener has to be disabled while 121 // JTextArea.setText() is called. 122 final Document document = getTextComponent().getDocument(); 123 document.removeDocumentListener(this); 124 getTextComponent().setText(l); 125 revalidate(); 126 if (firstChangeSkipped) { 127 postEvent(new TextEvent(getTarget(), 128 TextEvent.TEXT_VALUE_CHANGED)); 129 } 130 document.addDocumentListener(this); 131 } 132 repaintPeer(); 133 } 134 135 @Override 136 public final int getSelectionStart() { 137 synchronized (getDelegateLock()) { 138 return getTextComponent().getSelectionStart(); 139 } 140 } 141 142 @Override 143 public final int getSelectionEnd() { 144 synchronized (getDelegateLock()) { 145 return getTextComponent().getSelectionEnd(); 146 } 147 } 148 149 @Override 150 public final void select(final int selStart, final int selEnd) { 151 synchronized (getDelegateLock()) { 152 getTextComponent().select(selStart, selEnd); 153 } 154 repaintPeer(); 155 } 156 157 @Override 158 public final void setCaretPosition(final int pos) { 159 synchronized (getDelegateLock()) { 160 getTextComponent().setCaretPosition(pos); 161 } 162 repaintPeer(); 163 } 164 165 @Override 166 public final int getCaretPosition() { 167 synchronized (getDelegateLock()) { 168 return getTextComponent().getCaretPosition(); 169 } 170 } 171 172 @Override 173 public final InputMethodRequests getInputMethodRequests() { 174 synchronized (getDelegateLock()) { 175 return getTextComponent().getInputMethodRequests(); 176 } 177 } 178 179 @Override 180 public final boolean isFocusable() { 181 return getTarget().isFocusable(); 182 } 183 184 protected final void revalidate() { 185 synchronized (getDelegateLock()) { 186 getTextComponent().invalidate(); 187 getDelegate().validate(); 188 } 189 } 190 191 private void sendTextEvent(final DocumentEvent e) { 192 postEvent(new TextEvent(getTarget(), TextEvent.TEXT_VALUE_CHANGED)); 193 synchronized (getDelegateLock()) { 194 revalidate(); 195 } 196 } 197 198 @Override 199 public final void changedUpdate(final DocumentEvent e) { 200 sendTextEvent(e); 201 } 202 203 @Override 204 public final void insertUpdate(final DocumentEvent e) { 205 sendTextEvent(e); 206 } 207 208 @Override 209 public final void removeUpdate(final DocumentEvent e) { 210 sendTextEvent(e); 211 } 212 213 @Override 214 public final void inputMethodTextChanged(final InputMethodEvent event) { 215 synchronized (getDelegateLock()) { 216 AWTAccessor.getComponentAccessor().processEvent(getTextComponent(), event); 217 } 218 } 219 220 @Override 221 public final void caretPositionChanged(final InputMethodEvent event) { 222 synchronized (getDelegateLock()) { 223 AWTAccessor.getComponentAccessor().processEvent(getTextComponent(), event); 224 } 225 } 226 227 /** 228 * Restoring native behavior. We should sets the selection range to zero, 229 * when component lost its focus. 230 * 231 * @param e the focus event 232 */ 233 @Override 234 protected final void handleJavaFocusEvent(final FocusEvent e) { 235 if (e.getID() == FocusEvent.FOCUS_LOST) { 236 // In order to de-select the selection 237 setCaretPosition(getCaretPosition()); 238 } 239 super.handleJavaFocusEvent(e); 240 } 241 }