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