/*
 * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.awt.X11;

import java.awt.*;
import java.awt.peer.ComponentPeer;
import java.awt.peer.TextAreaPeer;
import java.awt.event.*;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.JTextArea;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JScrollBar;
import javax.swing.plaf.ComponentUI;
import com.sun.java.swing.plaf.motif.MotifTextAreaUI;
import javax.swing.plaf.UIResource;
import javax.swing.UIDefaults;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.CompoundBorder;
import javax.swing.border.AbstractBorder;
import javax.swing.JButton;
import javax.swing.JViewport;
import javax.swing.InputMap;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicScrollBarUI;
import javax.swing.plaf.basic.BasicScrollPaneUI;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
import javax.swing.text.JTextComponent;

import javax.swing.plaf.BorderUIResource;
import java.awt.im.InputMethodRequests;
import sun.awt.CausedFocusEvent;
import sun.awt.ComponentAccessor;
import sun.awt.AWTAccessor;


class XTextAreaPeer extends XComponentPeer implements TextAreaPeer {
    boolean editable;

    AWTTextPane textPane;
    AWTTextArea jtext;

    boolean firstChangeSkipped;

    private final JavaMouseEventHandler javaMouseEventHandler
        = new JavaMouseEventHandler( this );

    /* FIXME  */

    public long filterEvents(long mask) {
        Thread.dumpStack();
        return 0;
    }

    /* FIXME   */
    public Rectangle getCharacterBounds(int i) {
        Thread.dumpStack();
        return null;
    }

    public int getIndexAtPoint(int x, int y) {
        Thread.dumpStack();
        return 0;
    }


    
Create a Text area.
/** * Create a Text area. */
XTextAreaPeer(TextArea target) { super( target ); // some initializations require that target be set even // though init(target) has not been called this.target = target; //ComponentAccessor.enableEvents(target,AWTEvent.MOUSE_WHEEL_EVENT_MASK); target.enableInputMethods(true); firstChangeSkipped = false; String text = ((TextArea)target).getText(); jtext = new AWTTextArea(text, this); jtext.setWrapStyleWord(true); jtext.getDocument().addDocumentListener(jtext); XToolkit.specialPeerMap.put(jtext,this); jtext.enableInputMethods(true); textPane = new AWTTextPane(jtext,this, target.getParent()); setBounds(x, y, width, height, SET_BOUNDS); textPane.setVisible(true); textPane.validate(); foreground = ComponentAccessor.getForeground(target); if (foreground == null) { foreground = SystemColor.textText; } setForeground(foreground); background = ComponentAccessor.getBackground(target); if (background == null) { if (target.isEditable()) background = SystemColor.text; else background = SystemColor.control; } setBackground(background); if (!target.isBackgroundSet()) { // This is a way to set the background color of the TextArea // without calling setBackground - go through reflection ComponentAccessor.setBackground(target, background); } if (!target.isForegroundSet()) { target.setForeground(SystemColor.textText); } setFont(font); int start = target.getSelectionStart(); int end = target.getSelectionEnd(); if (end > start) { select(start, end); } // Fix for 5100200 // Restoring Motif behaviour // Since the end position of the selected text can be greater then the length of the text, // so we should set caret to max position of the text int caretPosition = Math.min(end, text.length()); setCaretPosition(caretPosition); setEditable(target.isEditable()); setScrollBarVisibility(); // set the text of this object to the text of its target setTextImpl(target.getText()); //?? should this be setText // After this line we should not change the component's text firstChangeSkipped = true; } public void dispose() { XToolkit.specialPeerMap.remove(jtext); jtext.removeNotify(); textPane.removeNotify(); super.dispose(); } void setScrollBarVisibility() { int visibility = ((TextArea)target).getScrollbarVisibility(); jtext.setLineWrap(false); if (visibility == TextArea.SCROLLBARS_NONE) { textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); jtext.setLineWrap(true); } else if (visibility == TextArea.SCROLLBARS_BOTH) { textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); } else if (visibility == TextArea.SCROLLBARS_VERTICAL_ONLY) { textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); jtext.setLineWrap(true); } else if (visibility == TextArea.SCROLLBARS_HORIZONTAL_ONLY) { textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); } }
Compute minimum size.
/** * Compute minimum size. */
public Dimension getMinimumSize() { return getMinimumSize(10, 60); } public Dimension getPreferredSize(int rows, int cols) { return getMinimumSize(rows, cols); }
See Also:
  • TextAreaPeer
/** * @see java.awt.peer.TextAreaPeer */
public Dimension getMinimumSize(int rows, int cols) { /* Dimension d = null; if (jtext != null) { d = jtext.getMinimumSize(rows,cols); } return d; */ int vsbwidth=0; int hsbheight=0; JScrollBar vsb = textPane.getVerticalScrollBar(); if (vsb != null) { vsbwidth = vsb.getMinimumSize().width; } JScrollBar hsb = textPane.getHorizontalScrollBar(); if (hsb != null) { hsbheight = hsb.getMinimumSize().height; } Font f = jtext.getFont(); FontMetrics fm = jtext.getFontMetrics(f); return new Dimension(fm.charWidth('0') * cols + /*2*XMARGIN +*/ vsbwidth, fm.getHeight() * rows + /*2*YMARGIN +*/ hsbheight); } public boolean isFocusable() { return true; } public void setVisible(boolean b) { super.setVisible(b); if (textPane != null) textPane.setVisible(b); } void repaintText() { jtext.repaintNow(); } public void focusGained(FocusEvent e) { super.focusGained(e); jtext.forwardFocusGained(e); } public void focusLost(FocusEvent e) { super.focusLost(e); jtext.forwardFocusLost(e); }
Paint the component this method is called when the repaint instruction has been used
/** * Paint the component * this method is called when the repaint instruction has been used */
public void repaint() { if (textPane != null) { //textPane.validate(); textPane.repaint(); } } public void paint(Graphics g) { if (textPane != null) { textPane.paint(g); } } public void setBounds(int x, int y, int width, int height, int op) { super.setBounds(x, y, width, height, op); if (textPane != null) { /* * Fixed 6277332, 6198290: * the coordinates is coming (to peer): relatively to closest HW parent * the coordinates is setting (to textPane): relatively to closest ANY parent * the parent of peer is target.getParent() * the parent of textPane is the same * see 6277332, 6198290 for more information */ int childX = x; int childY = y; Component parent = target.getParent(); // we up to heavyweight parent in order to be sure // that the coordinates of the text pane is relatively to closest parent while (parent.isLightweight()){ childX -= parent.getX(); childY -= parent.getY(); parent = parent.getParent(); } textPane.setBounds(childX,childY,width,height); textPane.validate(); } } void handleJavaKeyEvent(KeyEvent e) { ComponentAccessor.processEvent(jtext,e); } public boolean handlesWheelScrolling() { return true; } void handleJavaMouseWheelEvent(MouseWheelEvent e) { ComponentAccessor.processEvent(textPane,e); } public void handleJavaMouseEvent( MouseEvent e ) { super.handleJavaMouseEvent( e ); javaMouseEventHandler.handle( e ); } void handleJavaInputMethodEvent(InputMethodEvent e) { if (jtext != null) jtext.processInputMethodEventPublic((InputMethodEvent)e); }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public void select(int s, int e) { jtext.select(s,e); // Fixed 5100806 // We must take care that Swing components repainted correctly jtext.repaint(); } public void setBackground(Color c) { super.setBackground(c); // synchronized (getStateLock()) { // background = c; // } if (jtext != null) { jtext.setBackground(c); jtext.setSelectedTextColor(c); } // repaintText(); } public void setForeground(Color c) { super.setForeground(c); // synchronized (getStateLock()) { // foreground = c; // } if (jtext != null) { jtext.setForeground(foreground); jtext.setSelectionColor(foreground); jtext.setCaretColor(foreground); } // repaintText(); } public void setFont(Font f) { super.setFont(f); // synchronized (getStateLock()) { // font = f; // } if (jtext != null) { jtext.setFont(font); } textPane.validate(); }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public void setEditable(boolean editable) { this.editable = editable; if (jtext != null) jtext.setEditable(editable); repaintText(); }
See Also:
  • ComponentPeer
/** * @see java.awt.peer.ComponentPeer */
public void setEnabled(boolean enabled) { super.setEnabled(enabled); if (jtext != null) { jtext.setEnabled(enabled); jtext.repaint(); } }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public InputMethodRequests getInputMethodRequests() { if (jtext != null) return jtext.getInputMethodRequests(); else return null; }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public int getSelectionStart() { return jtext.getSelectionStart(); }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public int getSelectionEnd() { return jtext.getSelectionEnd(); }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public String getText() { return jtext.getText(); }
See Also:
  • TextComponentPeer
/** * @see java.awt.peer.TextComponentPeer */
public void setText(String txt) { setTextImpl(txt); repaintText(); } protected boolean setTextImpl(String txt) { if (jtext != null) { // Please note that we do not want to post an event // if setText() replaces an empty text by an empty text, // that is, if component's text remains unchanged. if (jtext.getDocument().getLength() == 0 && txt.length() == 0) { return true; } // JTextArea.setText() posts two different events (remove & insert). // Since we make no differences between text events, // the document listener has to be disabled while // JTextArea.setText() is called. jtext.getDocument().removeDocumentListener(jtext); jtext.setText(txt); if (firstChangeSkipped) { postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED)); } jtext.getDocument().addDocumentListener(jtext); } return true; }
insert the text "txt on position "pos" in the array lines
See Also:
  • TextAreaPeer
/** * insert the text "txt on position "pos" in the array lines * @see java.awt.peer.TextAreaPeer */
public void insert(String txt, int p) { if (jtext != null) { boolean doScroll = (p >= jtext.getDocument().getLength() && jtext.getDocument().getLength() != 0); jtext.insert(txt,p); textPane.validate(); if (doScroll) { JScrollBar bar = textPane.getVerticalScrollBar(); if (bar != null) { bar.setValue(bar.getMaximum()-bar.getVisibleAmount()); } } } }
replace the text between the position "s" and "e" with "txt"
See Also:
  • TextAreaPeer
/** * replace the text between the position "s" and "e" with "txt" * @see java.awt.peer.TextAreaPeer */
public void replaceRange(String txt, int s, int e) { if (jtext != null) { // JTextArea.replaceRange() posts two different events. // Since we make no differences between text events, // the document listener has to be disabled while // JTextArea.replaceRange() is called. jtext.getDocument().removeDocumentListener(jtext); jtext.replaceRange(txt, s, e); postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED)); jtext.getDocument().addDocumentListener(jtext); } }
to be implemented.
See Also:
  • TextComponentPeer
/** * to be implemented. * @see java.awt.peer.TextComponentPeer */
public void setCaretPosition(int position) { jtext.setCaretPosition(position); }
to be implemented.
See Also:
  • TextComponentPeer
/** * to be implemented. * @see java.awt.peer.TextComponentPeer */
public int getCaretPosition() { return jtext.getCaretPosition(); }
DEPRECATED
See Also:
  • TextAreaPeer
/** * DEPRECATED * @see java.awt.peer.TextAreaPeer */
public void insertText(String txt, int pos) { insert(txt, pos); }
DEPRECATED
See Also:
  • TextAreaPeer
/** * DEPRECATED * @see java.awt.peer.TextAreaPeer */
public void replaceText(String txt, int start, int end) { replaceRange(txt, start, end); }
DEPRECATED
See Also:
  • TextAreaPeer
/** * DEPRECATED * @see java.awt.peer.TextAreaPeer */
public Dimension minimumSize(int rows, int cols) { return getMinimumSize(rows, cols); }
DEPRECATED
See Also:
  • TextAreaPeer
/** * DEPRECATED * @see java.awt.peer.TextAreaPeer */
public Dimension preferredSize(int rows, int cols) { return getPreferredSize(rows, cols); } class AWTTextAreaUI extends MotifTextAreaUI {
Creates a UI for a JTextArea.
Params:
  • c – the text field
Returns:the UI
/** * Creates a UI for a JTextArea. * * @param c the text field * @return the UI */
JTextArea jta; protected String getPropertyPrefix() { return "TextArea"; } public void installUI(JComponent c) { super.installUI(c); jta = (JTextArea) c; JTextArea editor = jta; UIDefaults uidefaults = XToolkit.getUIDefaults(); String prefix = getPropertyPrefix(); Font f = editor.getFont(); if ((f == null) || (f instanceof UIResource)) { editor.setFont(uidefaults.getFont(prefix + ".font")); } Color bg = editor.getBackground(); if ((bg == null) || (bg instanceof UIResource)) { editor.setBackground(uidefaults.getColor(prefix + ".background")); } Color fg = editor.getForeground(); if ((fg == null) || (fg instanceof UIResource)) { editor.setForeground(uidefaults.getColor(prefix + ".foreground")); } Color color = editor.getCaretColor(); if ((color == null) || (color instanceof UIResource)) { editor.setCaretColor(uidefaults.getColor(prefix + ".caretForeground")); } Color s = editor.getSelectionColor(); if ((s == null) || (s instanceof UIResource)) { editor.setSelectionColor(uidefaults.getColor(prefix + ".selectionBackground")); } Color sfg = editor.getSelectedTextColor(); if ((sfg == null) || (sfg instanceof UIResource)) { editor.setSelectedTextColor(uidefaults.getColor(prefix + ".selectionForeground")); } Color dfg = editor.getDisabledTextColor(); if ((dfg == null) || (dfg instanceof UIResource)) { editor.setDisabledTextColor(uidefaults.getColor(prefix + ".inactiveForeground")); } Border b = new BevelBorder(false,SystemColor.controlDkShadow,SystemColor.controlLtHighlight); editor.setBorder(new BorderUIResource.CompoundBorderUIResource( b,new EmptyBorder(2, 2, 2, 2))); Insets margin = editor.getMargin(); if (margin == null || margin instanceof UIResource) { editor.setMargin(uidefaults.getInsets(prefix + ".margin")); } } protected void installKeyboardActions() { super.installKeyboardActions(); JTextComponent comp = getComponent(); UIDefaults uidefaults = XToolkit.getUIDefaults(); String prefix = getPropertyPrefix(); InputMap map = (InputMap)uidefaults.get(prefix + ".focusInputMap"); if (map != null) { SwingUtilities.replaceUIInputMap(comp, JComponent.WHEN_FOCUSED, map); } } protected Caret createCaret() { return new XAWTCaret(); } } // TODO : fix this duplicate code class XAWTCaret extends DefaultCaret { public void focusGained(FocusEvent e) { super.focusGained(e); getComponent().repaint(); } public void focusLost(FocusEvent e) { super.focusLost(e); getComponent().repaint(); } // Fix for 5100950: textarea.getSelectedText() returns the de-selected text, on XToolkit // Restoring Motif behaviour // If the text is unhighlighted then we should sets the selection range to zero public void setSelectionVisible(boolean vis) { if (vis){ super.setSelectionVisible(vis); }else{ // In order to de-select the selection setDot(getDot()); } } } class XAWTScrollBarButton extends BasicArrowButton { UIDefaults uidefaults = XToolkit.getUIDefaults(); private Color darkShadow = SystemColor.controlShadow; private Color lightShadow = SystemColor.controlLtHighlight; private Color buttonBack = uidefaults.getColor("ScrollBar.track"); public XAWTScrollBarButton(int direction) { super(direction); switch (direction) { case NORTH: case SOUTH: case EAST: case WEST: this.direction = direction; break; default: throw new IllegalArgumentException("invalid direction"); } setRequestFocusEnabled(false); setOpaque(true); setBackground(uidefaults.getColor("ScrollBar.thumb")); setForeground(uidefaults.getColor("ScrollBar.foreground")); } public Dimension getPreferredSize() { switch (direction) { case NORTH: case SOUTH: return new Dimension(11, 12); case EAST: case WEST: default: return new Dimension(12, 11); } } public Dimension getMinimumSize() { return getPreferredSize(); } public Dimension getMaximumSize() { return getPreferredSize(); } public boolean isFocusTraversable() { return false; } public void paint(Graphics g) { int w = getWidth(); int h = getHeight(); if (isOpaque()) { g.setColor(buttonBack); g.fillRect(0, 0, w, h); } boolean isPressed = getModel().isPressed(); Color lead = (isPressed) ? darkShadow : lightShadow; Color trail = (isPressed) ? lightShadow : darkShadow; Color fill = getBackground(); int cx = w / 2; int cy = h / 2; int s = Math.min(w, h); switch (direction) { case NORTH: g.setColor(lead); g.drawLine(cx, 0, cx, 0); for (int x = cx - 1, y = 1, dx = 1; y <= s - 2; y += 2) { g.setColor(lead); g.drawLine(x, y, x, y); if (y >= (s - 2)) { g.drawLine(x, y + 1, x, y + 1); } g.setColor(fill); g.drawLine(x + 1, y, x + dx, y); if (y < (s - 2)) { g.drawLine(x, y + 1, x + dx + 1, y + 1); } g.setColor(trail); g.drawLine(x + dx + 1, y, x + dx + 1, y); if (y >= (s - 2)) { g.drawLine(x + 1, y + 1, x + dx + 1, y + 1); } dx += 2; x -= 1; } break; case SOUTH: g.setColor(trail); g.drawLine(cx, s, cx, s); for (int x = cx - 1, y = s - 1, dx = 1; y >= 1; y -= 2) { g.setColor(lead); g.drawLine(x, y, x, y); if (y <= 2) { g.drawLine(x, y - 1, x + dx + 1, y - 1); } g.setColor(fill); g.drawLine(x + 1, y, x + dx, y); if (y > 2) { g.drawLine(x, y - 1, x + dx + 1, y - 1); } g.setColor(trail); g.drawLine(x + dx + 1, y, x + dx + 1, y); dx += 2; x -= 1; } break; case EAST: g.setColor(lead); g.drawLine(s, cy, s, cy); for (int y = cy - 1, x = s - 1, dy = 1; x >= 1; x -= 2) { g.setColor(lead); g.drawLine(x, y, x, y); if (x <= 2) { g.drawLine(x - 1, y, x - 1, y + dy + 1); } g.setColor(fill); g.drawLine(x, y + 1, x, y + dy); if (x > 2) { g.drawLine(x - 1, y, x - 1, y + dy + 1); } g.setColor(trail); g.drawLine(x, y + dy + 1, x, y + dy + 1); dy += 2; y -= 1; } break; case WEST: g.setColor(trail); g.drawLine(0, cy, 0, cy); for (int y = cy - 1, x = 1, dy = 1; x <= s - 2; x += 2) { g.setColor(lead); g.drawLine(x, y, x, y); if (x >= (s - 2)) { g.drawLine(x + 1, y, x + 1, y); } g.setColor(fill); g.drawLine(x, y + 1, x, y + dy); if (x < (s - 2)) { g.drawLine(x + 1, y, x + 1, y + dy + 1); } g.setColor(trail); g.drawLine(x, y + dy + 1, x, y + dy + 1); if (x >= (s - 2)) { g.drawLine(x + 1, y + 1, x + 1, y + dy + 1); } dy += 2; y -= 1; } break; } } } class XAWTScrollBarUI extends BasicScrollBarUI { public XAWTScrollBarUI() { super(); } protected void installDefaults() { super.installDefaults(); scrollbar.setBorder(new BevelBorder(false,SystemColor.controlDkShadow,SystemColor.controlLtHighlight) ); } protected void configureScrollBarColors() { UIDefaults uidefaults = XToolkit.getUIDefaults(); Color bg = scrollbar.getBackground(); if (bg == null || bg instanceof UIResource) { scrollbar.setBackground(uidefaults.getColor("ScrollBar.background")); } Color fg = scrollbar.getForeground(); if (fg == null || fg instanceof UIResource) { scrollbar.setForeground(uidefaults.getColor("ScrollBar.foreground")); } thumbHighlightColor = uidefaults.getColor("ScrollBar.thumbHighlight"); thumbLightShadowColor = uidefaults.getColor("ScrollBar.thumbShadow"); thumbDarkShadowColor = uidefaults.getColor("ScrollBar.thumbDarkShadow"); thumbColor = uidefaults.getColor("ScrollBar.thumb"); trackColor = uidefaults.getColor("ScrollBar.track"); trackHighlightColor = uidefaults.getColor("ScrollBar.trackHighlight"); } protected JButton createDecreaseButton(int orientation) { JButton b = new XAWTScrollBarButton(orientation); return b; } protected JButton createIncreaseButton(int orientation) { JButton b = new XAWTScrollBarButton(orientation); return b; } public JButton getDecreaseButton(){ return decrButton; } public JButton getIncreaseButton(){ return incrButton; } public void paint(Graphics g, JComponent c) { paintTrack(g, c, getTrackBounds()); Rectangle thumbBounds = getThumbBounds(); paintThumb(g, c, thumbBounds); } public void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { if(!scrollbar.isEnabled()) { return; } if (thumbBounds.isEmpty()) thumbBounds = getTrackBounds(); int w = thumbBounds.width; int h = thumbBounds.height; g.translate(thumbBounds.x, thumbBounds.y); g.setColor(thumbColor); g.fillRect(0, 0, w-1, h-1); g.setColor(thumbHighlightColor); g.drawLine(0, 0, 0, h-1); g.drawLine(1, 0, w-1, 0); g.setColor(thumbLightShadowColor); g.drawLine(1, h-1, w-1, h-1); g.drawLine(w-1, 1, w-1, h-2); g.translate(-thumbBounds.x, -thumbBounds.y); } } class AWTTextArea extends JTextArea implements DocumentListener { boolean isFocused = false; XTextAreaPeer peer; public AWTTextArea(String text, XTextAreaPeer peer) { super(text); setFocusable(false); this.peer = peer; } public void insertUpdate(DocumentEvent e) { if (peer != null) { peer.postEvent(new TextEvent(peer.target, TextEvent.TEXT_VALUE_CHANGED)); } } public void removeUpdate(DocumentEvent e) { if (peer != null) { peer.postEvent(new TextEvent(peer.target, TextEvent.TEXT_VALUE_CHANGED)); } } public void changedUpdate(DocumentEvent e) { if (peer != null) { peer.postEvent(new TextEvent(peer.target, TextEvent.TEXT_VALUE_CHANGED)); } } void forwardFocusGained( FocusEvent e) { isFocused = true; FocusEvent fe = CausedFocusEvent.retarget(e, this); super.processFocusEvent(fe); } void forwardFocusLost( FocusEvent e) { isFocused = false; FocusEvent fe = CausedFocusEvent.retarget(e, this); super.processFocusEvent(fe); } public boolean hasFocus() { return isFocused; } public void repaintNow() { paintImmediately(getBounds()); } public void processMouseEventPublic(MouseEvent e) { processMouseEvent(e); } public void processMouseMotionEventPublic(MouseEvent e) { processMouseMotionEvent(e); } public void processInputMethodEventPublic(InputMethodEvent e) { processInputMethodEvent(e); } public void updateUI() { ComponentUI ui = new AWTTextAreaUI(); setUI(ui); } // Fix for 4915454 - override the default implementation to avoid // loading SystemFlavorMap and associated classes. public void setTransferHandler(TransferHandler newHandler) { TransferHandler oldHandler = (TransferHandler) getClientProperty(AWTAccessor.getClientPropertyKeyAccessor() .getJComponent_TRANSFER_HANDLER()); putClientProperty(AWTAccessor.getClientPropertyKeyAccessor() .getJComponent_TRANSFER_HANDLER(), newHandler); firePropertyChange("transferHandler", oldHandler, newHandler); } } class XAWTScrollPaneUI extends BasicScrollPaneUI { private final Border vsbMarginBorderR = new EmptyBorder(0, 2, 0, 0); private final Border vsbMarginBorderL = new EmptyBorder(0, 0, 0, 2); private final Border hsbMarginBorder = new EmptyBorder(2, 0, 0, 0); private Border vsbBorder; private Border hsbBorder; private PropertyChangeListener propertyChangeHandler; protected void installListeners(JScrollPane scrollPane) { super.installListeners(scrollPane); propertyChangeHandler = createPropertyChangeHandler(); scrollPane.addPropertyChangeListener(propertyChangeHandler); } public void paint(Graphics g, JComponent c) { Border vpBorder = scrollpane.getViewportBorder(); if (vpBorder != null) { Rectangle r = scrollpane.getViewportBorderBounds(); vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height); } } protected void uninstallListeners(JScrollPane scrollPane) { super.uninstallListeners(scrollPane); scrollPane.removePropertyChangeListener(propertyChangeHandler); } private PropertyChangeListener createPropertyChangeHandler() { return new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String propertyName = e.getPropertyName(); if (propertyName.equals("componentOrientation")) { JScrollPane pane = (JScrollPane)e.getSource(); JScrollBar vsb = pane.getVerticalScrollBar(); if (vsb != null) { if (isLeftToRight(pane)) { vsbBorder = new CompoundBorder(new EmptyBorder(0, 4, 0, -4), vsb.getBorder()); } else { vsbBorder = new CompoundBorder(new EmptyBorder(0, -4, 0, 4), vsb.getBorder()); } vsb.setBorder(vsbBorder); } } }}; } boolean isLeftToRight( Component c ) { return c.getComponentOrientation().isLeftToRight(); } protected void installDefaults(JScrollPane scrollpane) { Border b = scrollpane.getBorder(); UIDefaults uidefaults = XToolkit.getUIDefaults(); scrollpane.setBorder(uidefaults.getBorder("ScrollPane.border")); scrollpane.setBackground(uidefaults.getColor("ScrollPane.background")); scrollpane.setViewportBorder(uidefaults.getBorder("TextField.border")); JScrollBar vsb = scrollpane.getVerticalScrollBar(); if (vsb != null) { if (isLeftToRight(scrollpane)) { vsbBorder = new CompoundBorder(vsbMarginBorderR, vsb.getBorder()); } else { vsbBorder = new CompoundBorder(vsbMarginBorderL, vsb.getBorder()); } vsb.setBorder(vsbBorder); } JScrollBar hsb = scrollpane.getHorizontalScrollBar(); if (hsb != null) { hsbBorder = new CompoundBorder(hsbMarginBorder, hsb.getBorder()); hsb.setBorder(hsbBorder); } } protected void uninstallDefaults(JScrollPane c) { super.uninstallDefaults(c); JScrollBar vsb = scrollpane.getVerticalScrollBar(); if (vsb != null) { if (vsb.getBorder() == vsbBorder) { vsb.setBorder(null); } vsbBorder = null; } JScrollBar hsb = scrollpane.getHorizontalScrollBar(); if (hsb != null) { if (hsb.getBorder() == hsbBorder) { hsb.setBorder(null); } hsbBorder = null; } } } private class AWTTextPane extends JScrollPane implements FocusListener { JTextArea jtext; XWindow xwin; Color control = SystemColor.control; Color focus = SystemColor.activeCaptionBorder; public AWTTextPane(JTextArea jt, XWindow xwin, Container parent) { super(jt); this.xwin = xwin; setDoubleBuffered(true); jt.addFocusListener(this); ComponentAccessor.setParent(this,parent); setViewportBorder(new BevelBorder(false,SystemColor.controlDkShadow,SystemColor.controlLtHighlight) ); this.jtext = jt; setFocusable(false); addNotify(); } public void focusGained(FocusEvent e) { Graphics g = getGraphics(); Rectangle r = getViewportBorderBounds(); g.setColor(focus); g.drawRect(r.x,r.y,r.width,r.height); g.dispose(); } public void focusLost(FocusEvent e) { Graphics g = getGraphics(); Rectangle r = getViewportBorderBounds(); g.setColor(control); g.drawRect(r.x,r.y,r.width,r.height); g.dispose(); } public Window getRealParent() { return (Window) xwin.target; } public ComponentPeer getPeer() { return (ComponentPeer) (xwin); } public void updateUI() { ComponentUI ui = new XAWTScrollPaneUI(); setUI(ui); } public JScrollBar createVerticalScrollBar() { return new XAWTScrollBar(JScrollBar.VERTICAL); } public JScrollBar createHorizontalScrollBar() { return new XAWTScrollBar(JScrollBar.HORIZONTAL); } public JTextArea getTextArea () { return this.jtext; } public Graphics getGraphics() { return xwin.getGraphics(); } class XAWTScrollBar extends ScrollBar { public XAWTScrollBar(int i) { super(i); setFocusable(false); } public void updateUI() { ComponentUI ui = new XAWTScrollBarUI(); setUI(ui); } } } static class BevelBorder extends AbstractBorder implements UIResource { private Color darkShadow = SystemColor.controlDkShadow; private Color lightShadow = SystemColor.controlLtHighlight; private Color control = SystemColor.controlShadow; private boolean isRaised; public BevelBorder(boolean isRaised, Color darkShadow, Color lightShadow) { this.isRaised = isRaised; this.darkShadow = darkShadow; this.lightShadow = lightShadow; } public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { g.setColor((isRaised) ? lightShadow : darkShadow); g.drawLine(x, y, x+w-1, y); // top g.drawLine(x, y+h-1, x, y+1); // left g.setColor(control); g.drawLine(x+1, y+1, x+w-2, y+1); // top g.drawLine(x+1, y+h-1, x+1, y+1); // left g.setColor((isRaised) ? darkShadow : lightShadow); g.drawLine(x+1, y+h-1, x+w-1, y+h-1); // bottom g.drawLine(x+w-1, y+h-1, x+w-1, y+1); // right g.setColor(control); g.drawLine(x+1, y+h-2, x+w-2, y+h-2); // bottom g.drawLine(x+w-2, y+h-2, x+w-2, y+1); // right } public Insets getBorderInsets(Component c) { return getBorderInsets(c, new Insets(0,0,0,0)); } public Insets getBorderInsets(Component c, Insets insets) { insets.top = insets.left = insets.bottom = insets.right = 2; return insets; } public boolean isOpaque(Component c) { return true; } } // This class dispatches 'MouseEvent's to 'XTextAreaPeer''s (hidden) // subcomponents, and overrides mouse cursor, e.g. for scrollbars. // // However, current dispatching is a kind of fake, and is tuned to do only // what is necessary/possible. E.g. no additional mouse-exited/entered // events are generated, when mouse exits scrollbar and enters viewport // with JTextArea inside. Actually, no events are ever generated here (for // now). They are only dispatched as correctly as possible/neccessary. // // In future, it would be better to replace fake-emulation of grab-detection // and event-dispatching here, by reusing some common implementation of this // functionality. Mouse-cursor setting should also be freed of hacked // overloading here. private static final class JavaMouseEventHandler { private final XTextAreaPeer outer; private final Pointer current = new Pointer(); private boolean grabbed = false; JavaMouseEventHandler( XTextAreaPeer outer ) { this.outer = outer; } // 1. We can make grab-tracking emulation here more robust to variations in // in mouse-events order and consistence. E.g. by using such code: // if( grabbed && event.getID()==MouseEvent.MOUSE_MOVED ) grabbed = false; // Or we can also use 'assert'ions. // 2. WARNING: Currently, while grab-detection mechanism _here_ says, that // grab is in progress, we do not update 'current'. In case 'current' // is set to a scrollbar or to a scroll-button, then references to their // 'Component'-instances are "remembered". And events are dispatched to // these remembered components, without checking, if XTextAreaPeer has // replaced these instances with another ones. This also aplies to // mouse-drags-from-outside (see comment in 'grabbed_update' method). void handle( MouseEvent event ) { if ( ! grabbed ) { // dispatch() needs up-to-date pointer in ungrabbed case. setPointerToUnderEventPoint( event ); } dispatch( event ); boolean wasGrabbed = grabbed; grabbed_update( event ); if ( wasGrabbed && ! grabbed ) { setPointerToUnderEventPoint( event ); } setCursor(); } // Following is internally private: // Here dispatching is performed, of 'MouseEvent's to (some) // 'XTextAreaPeer''s (hidden) subcomponents. private void dispatch( MouseEvent event ) { switch( current.getType() ) { case TEXT: Point point = toViewportChildLocalSpace( outer.textPane.getViewport(), event.getPoint() ); XTextAreaPeer.AWTTextArea jtext = outer.jtext; MouseEvent newEvent = newMouseEvent( jtext, point, event ); int id = newEvent.getID(); if ( id==MouseEvent.MOUSE_MOVED || id==MouseEvent.MOUSE_DRAGGED ) { jtext.processMouseMotionEventPublic( newEvent ); } else { jtext.processMouseEventPublic( newEvent ); } break; // We perform (additional) dispatching of events to buttons of // scrollbar, instead of leaving it to JScrollbar. This is // required, because of different listeners in Swing and AWT, // which trigger scrolling (ArrowButtonListener vs. TrackListener, // accordingly). So we dispatch events to scroll-buttons, to // invoke a correct Swing button listener. // See CR 6175401 for more information. case BAR: case BUTTON: Component c = current.getBar(); Point p = toLocalSpace( c, event.getPoint() ); if ( current.getType()==Pointer.Type.BUTTON ) { c = current.getButton(); p = toLocalSpace( c, p ); } ComponentAccessor.processEvent( c, newMouseEvent( c, p, event ) ); break; } } private static MouseEvent newMouseEvent( Component source, Point point, MouseEvent template ) { MouseEvent e = template; return new MouseEvent( source, e.getID(), e.getWhen(), e.getModifiersEx() | e.getModifiers(), point.x, point.y, e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton() ); } private void setCursor() { if ( current.getType()==Pointer.Type.TEXT ) { // 'target.getCursor()' is also applied from elsewhere // (at least now), but only when mouse "entered", and // before 'XTextAreaPeer.handleJavaMouseEvent' is invoked. outer.pSetCursor( outer.target.getCursor() ); } else { // We can write here a more intelligent cursor selection // mechanism, like getting cursor from 'current' component. // However, I see no point in doing so now. But if you feel // like implementing it, you'll probably need to introduce // 'Pointer.Type.PANEL'. outer.pSetCursor( outer.textPane.getCursor() ); } } // Current way of grab-detection causes interesting (but harmless) // side-effect. If mouse is draged from outside to inside of TextArea, // we will then (in some cases) be asked to dispatch mouse-entered/exited // events. But, as at least one mouse-button is down, we will detect // grab-mode is on (though the grab isn't ours). // // Thus, we will not update 'current' (see 'handle' method), and will // dispatch events to the last subcomponent, the 'current' was set to. // As always, we set cursor in this case also. But, all this seems // harmless, because mouse entered/exited events seem to have no effect // here, and cursor setting is ignored in case of drags from outside. // // Grab-detection can be further improved, e.g. by taking into account // current event-ID, but I see not point in doing it now. private void grabbed_update( MouseEvent event ) { final int allButtonsMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK; grabbed = ( (event.getModifiersEx() & allButtonsMask) != 0 ); } // 'toLocalSpace' and 'toViewportChildLocalSpace' can be "optimized" to // 'return' 'void' and use 'Point' input-argument also as output. private static Point toLocalSpace( Component local, Point inParentSpace ) { Point p = inParentSpace; Point l = local.getLocation(); return new Point( p.x - l.x, p.y - l.y ); } private static Point toViewportChildLocalSpace( JViewport v, Point inViewportParentSpace ) { Point l = toLocalSpace(v, inViewportParentSpace); Point p = v.getViewPosition(); l.x += p.x; l.y += p.y; return l; } private void setPointerToUnderEventPoint( MouseEvent event ) { Point point = event.getPoint(); if ( outer.textPane.getViewport().getBounds().contains( point ) ) { current.setText(); } else if ( ! setPointerIfPointOverScrollbar( outer.textPane.getVerticalScrollBar(), point ) ) { if ( ! setPointerIfPointOverScrollbar( outer.textPane.getHorizontalScrollBar(), point ) ) { current.setNone(); } } } private boolean setPointerIfPointOverScrollbar( JScrollBar bar, Point point ) { if ( ! bar.getBounds().contains( point ) ) { return false; } current.setBar( bar ); Point local = toLocalSpace( bar, point ); XTextAreaPeer.XAWTScrollBarUI ui = (XTextAreaPeer.XAWTScrollBarUI) bar.getUI(); if ( ! setPointerIfPointOverButton( ui.getIncreaseButton(), local ) ) { setPointerIfPointOverButton( ui.getDecreaseButton(), local ); } return true; } private boolean setPointerIfPointOverButton( JButton button, Point point ) { if ( ! button.getBounds().contains( point ) ) { return false; } current.setButton( button ); return true; } private static final class Pointer { static enum Type { NONE, TEXT, BAR, BUTTON // , PANEL } Type getType() { return type; } boolean isNone() { return type==Type.NONE; } JScrollBar getBar() { boolean ok = type==Type.BAR || type==Type.BUTTON; assert ok; return ok ? bar : null; } JButton getButton() { boolean ok = type==Type.BUTTON; assert ok; return ok ? button : null; } void setNone() { type = Type.NONE; } void setText() { type = Type.TEXT; } void setBar( JScrollBar bar ) { this.bar=bar; type=Type.BAR; } void setButton( JButton button ) { this.button=button; type=Type.BUTTON; } private Type type; private JScrollBar bar; private JButton button; } } }