1 /*
   2  * Copyright (c) 2011, 2015, 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 package sun.lwawt;
  27 
  28 import java.awt.Component;
  29 import java.awt.Cursor;
  30 import java.awt.Dimension;
  31 import java.awt.Insets;
  32 import java.awt.Point;
  33 import java.awt.TextArea;
  34 import java.awt.event.TextEvent;
  35 import java.awt.peer.TextAreaPeer;
  36 
  37 import javax.swing.JScrollBar;
  38 import javax.swing.JScrollPane;
  39 import javax.swing.JTextArea;
  40 import javax.swing.ScrollPaneConstants;
  41 import javax.swing.text.Document;
  42 
  43 /**
  44  * Lightweight implementation of {@link TextAreaPeer}. Delegates most of the
  45  * work to the {@link JTextArea} inside {@link JScrollPane}.
  46  */
  47 final class LWTextAreaPeer
  48         extends LWTextComponentPeer<TextArea, LWTextAreaPeer.ScrollableJTextArea>
  49         implements TextAreaPeer {
  50 
  51     /**
  52      * The default number of visible columns.
  53      */
  54     private static final int DEFAULT_COLUMNS = 60;
  55 
  56     /**
  57      * The default number of visible rows.
  58      */
  59     private static final int DEFAULT_ROWS = 10;
  60 
  61     LWTextAreaPeer(final TextArea target,
  62                    final PlatformComponent platformComponent) {
  63         super(target, platformComponent);
  64     }
  65 
  66     @Override
  67     ScrollableJTextArea createDelegate() {
  68         return new ScrollableJTextArea();
  69     }
  70 
  71     @Override
  72     void initializeImpl() {
  73         super.initializeImpl();
  74         final int visibility = getTarget().getScrollbarVisibility();
  75         synchronized (getDelegateLock()) {
  76             getTextComponent().setWrapStyleWord(true);
  77             setScrollBarVisibility(visibility);
  78         }
  79     }
  80 
  81     @Override
  82     JTextArea getTextComponent() {
  83         return getDelegate().getView();
  84     }
  85 
  86     @Override
  87     Cursor getCursor(final Point p) {
  88         final boolean isContains;
  89         synchronized (getDelegateLock()) {
  90             isContains = getDelegate().getViewport().getBounds().contains(p);
  91         }
  92         return isContains ? super.getCursor(p) : null;
  93     }
  94 
  95     @Override
  96     Component getDelegateFocusOwner() {
  97         return getTextComponent();
  98     }
  99 
 100     @Override
 101     public Dimension getPreferredSize() {
 102         return getMinimumSize();
 103     }
 104 
 105     @Override
 106     public Dimension getMinimumSize() {
 107         return getMinimumSize(DEFAULT_ROWS, DEFAULT_COLUMNS);
 108     }
 109 
 110     @Override
 111     public Dimension getPreferredSize(final int rows, final int columns) {
 112         return getMinimumSize(rows, columns);
 113     }
 114 
 115     @Override
 116     public Dimension getMinimumSize(final int rows, final int columns) {
 117         final Dimension size = super.getMinimumSize(rows, columns);
 118         synchronized (getDelegateLock()) {
 119             // JScrollPane insets
 120             final Insets pi = getDelegate().getInsets();
 121             size.width += pi.left + pi.right;
 122             size.height += pi.top + pi.bottom;
 123             // Take scrollbars into account.
 124             final int vsbPolicy = getDelegate().getVerticalScrollBarPolicy();
 125             if (vsbPolicy == ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS) {
 126                 final JScrollBar vbar = getDelegate().getVerticalScrollBar();
 127                 size.width += vbar != null ? vbar.getMinimumSize().width : 0;
 128             }
 129             final int hsbPolicy = getDelegate().getHorizontalScrollBarPolicy();
 130             if (hsbPolicy == ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS) {
 131                 final JScrollBar hbar = getDelegate().getHorizontalScrollBar();
 132                 size.height += hbar != null ? hbar.getMinimumSize().height : 0;
 133             }
 134         }
 135         return size;
 136     }
 137 
 138     @Override
 139     public void insert(final String text, final int pos) {
 140         final ScrollableJTextArea pane = getDelegate();
 141         synchronized (getDelegateLock()) {
 142             final JTextArea area = pane.getView();
 143             final boolean doScroll = pos >= area.getDocument().getLength()
 144                                      && area.getDocument().getLength() != 0;
 145             area.insert(text, pos);
 146             revalidate();
 147             if (doScroll) {
 148                 final JScrollBar vbar = pane.getVerticalScrollBar();
 149                 if (vbar != null) {
 150                     vbar.setValue(vbar.getMaximum() - vbar.getVisibleAmount());
 151                 }
 152             }
 153         }
 154         repaintPeer();
 155     }
 156 
 157     @Override
 158     public void replaceRange(final String text, final int start,
 159                              final int end) {
 160         synchronized (getDelegateLock()) {
 161             // JTextArea.replaceRange() posts two different events.
 162             // Since we make no differences between text events,
 163             // the document listener has to be disabled while
 164             // JTextArea.replaceRange() is called.
 165             final Document document = getTextComponent().getDocument();
 166             document.removeDocumentListener(this);
 167             getTextComponent().replaceRange(text, start, end);
 168             revalidate();
 169             postEvent(new TextEvent(getTarget(), TextEvent.TEXT_VALUE_CHANGED));
 170             document.addDocumentListener(this);
 171         }
 172         repaintPeer();
 173     }
 174 
 175     private void setScrollBarVisibility(final int visibility) {
 176         final ScrollableJTextArea pane = getDelegate();
 177         final JTextArea view = pane.getView();
 178         view.setLineWrap(false);
 179 
 180         switch (visibility) {
 181             case TextArea.SCROLLBARS_NONE:
 182                 pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 183                 pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
 184                 view.setLineWrap(true);
 185                 break;
 186             case TextArea.SCROLLBARS_VERTICAL_ONLY:
 187                 pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 188                 pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 189                 view.setLineWrap(true);
 190                 break;
 191             case TextArea.SCROLLBARS_HORIZONTAL_ONLY:
 192                 pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
 193                 pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
 194                 break;
 195             default:
 196                 pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
 197                 pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 198                 break;
 199         }
 200     }
 201 
 202     @SuppressWarnings("serial")// Safe: outer class is non-serializable.
 203     final class ScrollableJTextArea extends JScrollPane {
 204 
 205         ScrollableJTextArea() {
 206             super();
 207             getViewport().setView(new JTextAreaDelegate());
 208         }
 209 
 210         public JTextArea getView() {
 211             return (JTextArea) getViewport().getView();
 212         }
 213 
 214         @Override
 215         public void setEnabled(final boolean enabled) {
 216             getViewport().getView().setEnabled(enabled);
 217             super.setEnabled(enabled);
 218         }
 219 
 220         private final class JTextAreaDelegate extends JTextArea {
 221 
 222             // Empty non private constructor was added because access to this
 223             // class shouldn't be emulated by a synthetic accessor method.
 224             JTextAreaDelegate() {
 225                 super();
 226             }
 227 
 228             @Override
 229             public void replaceSelection(String content) {
 230                 getDocument().removeDocumentListener(LWTextAreaPeer.this);
 231                 super.replaceSelection(content);
 232                 // post only one text event in this case
 233                 postTextEvent();
 234                 getDocument().addDocumentListener(LWTextAreaPeer.this);
 235             }
 236 
 237             @Override
 238             public boolean hasFocus() {
 239                 return getTarget().hasFocus();
 240             }
 241 
 242             @Override
 243             public Point getLocationOnScreen() {
 244                 return LWTextAreaPeer.this.getLocationOnScreen();
 245             }
 246         }
 247     }
 248 }