jdk/src/solaris/classes/sun/awt/X11/XTextFieldPeer.java
author yan
Mon, 06 Oct 2008 16:45:00 +0400
changeset 1966 12a51fb0db0d
parent 2 90ce3da70b43
child 3938 ef327bd847c0
permissions -rw-r--r--
5100701: Toolkit.getLockingKeyState() does not work on XToolkit, but works on Motif Summary: Does not work on Motif but works on XToolkit now; implemented using XQueryPointer. Reviewed-by: anthony

/*
 * Copyright 2003-2007 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package sun.awt.X11;

import java.awt.*;
import java.awt.peer.*;
import java.awt.event.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.TextEvent;
import javax.swing.text.*;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.plaf.ComponentUI;
import javax.swing.InputMap;
import javax.swing.JPasswordField;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;

import java.awt.event.MouseEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;

import javax.swing.plaf.UIResource;
import javax.swing.UIDefaults;
import javax.swing.JTextField;
import javax.swing.JComponent;
import javax.swing.border.Border;
import com.sun.java.swing.plaf.motif.*;
import java.awt.im.InputMethodRequests;

import java.util.logging.*;
import sun.awt.CausedFocusEvent;
import sun.awt.ComponentAccessor;

public class XTextFieldPeer extends XComponentPeer implements TextFieldPeer {
    private static final Logger log = Logger.getLogger("sun.awt.X11.XTextField");

    String text;
    XAWTTextField xtext;

    boolean firstChangeSkipped;

    public XTextFieldPeer(TextField target) {
        super(target);
        int start, end;
        firstChangeSkipped = false;
        text = target.getText();
        xtext = new XAWTTextField(text,this, target.getParent());
        xtext.getDocument().addDocumentListener(xtext);
        xtext.setCursor(target.getCursor());
        target.enableInputMethods(true);
        xtext.enableInputMethods(true);
        XToolkit.specialPeerMap.put(xtext,this);

        TextField txt = (TextField) target;
        initTextField();
        setText(txt.getText());
        if (txt.echoCharIsSet()) {
            setEchoChar(txt.getEchoChar());
        }
        else setEchoChar((char)0);

        start = txt.getSelectionStart();
        end = txt.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(txt.isEditable());

        // After this line we should not change the component's text
        firstChangeSkipped = true;
    }

    public void dispose() {
        XToolkit.specialPeerMap.remove(xtext);
        xtext.removeNotify();
        super.dispose();
    }

    void initTextField() {
        setVisible(target.isVisible());

        setBounds(x, y, width, height, SET_BOUNDS);

        foreground = ComponentAccessor.getForeground(target);
        if (foreground == null)
            foreground = SystemColor.textText;

        setForeground(foreground);

        background = ComponentAccessor.getBackground(target);
        if (background == null) {
            if (((TextField)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);
    }


    /**
     * @see java.awt.peer.TextComponentPeer
     */
    public void setEditable(boolean editable) {
        if (xtext != null) {
            xtext.setEditable(editable);
            xtext.repaint();
        }
    }

    /**
     * @see java.awt.peer.ComponentPeer
     */
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        if (xtext != null) {
            xtext.setEnabled(enabled);
            xtext.repaint();
        }
    }

    /**
     * @see java.awt.peer.TextComponentPeer
     */

    public InputMethodRequests getInputMethodRequests() {
        if (xtext != null) return xtext.getInputMethodRequests();
        else  return null;

    }

    void handleJavaInputMethodEvent(InputMethodEvent e) {
        if (xtext != null)
            xtext.processInputMethodEventImpl(e);
    }


    /**
     * @see java.awt.peer.TextFieldPeer
     */
    public void setEchoChar(char c) {
        if (xtext != null) {
            xtext.setEchoChar(c);
            xtext.putClientProperty("JPasswordField.cutCopyAllowed",
                    xtext.echoCharIsSet() ? Boolean.FALSE : Boolean.TRUE);
        }
    }

    /**
     * @see java.awt.peer.TextComponentPeer
     */
    public int getSelectionStart() {
        return xtext.getSelectionStart();
    }

    /**
     * @see java.awt.peer.TextComponentPeer
     */
    public int getSelectionEnd() {
        return xtext.getSelectionEnd();
    }

    /**
     * @see java.awt.peer.TextComponentPeer
     */
    public String getText() {
        return xtext.getText();
    }

    /**
     * @see java.awt.peer.TextComponentPeer
     */
    public void setText(String txt) {
        setXAWTTextField(txt);
        repaint();
    }

    protected boolean setXAWTTextField(String txt) {
        text = txt;
        if (xtext != null)  {
            // JTextField.setText() posts two different events (remove & insert).
            // Since we make no differences between text events,
            // the document listener has to be disabled while
            // JTextField.setText() is called.
            xtext.getDocument().removeDocumentListener(xtext);
            xtext.setText(txt);
            if (firstChangeSkipped) {
                postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED));
            }
            xtext.getDocument().addDocumentListener(xtext);
            xtext.setCaretPosition(0);
        }
        return true;
    }

    /**
     * to be implemented.
     * @see java.awt.peer.TextComponentPeer
     */
    public void setCaretPosition(int position) {
        if (xtext != null) xtext.setCaretPosition(position);
    }

    /**
     * DEPRECATED
     * @see java.awt.peer.TextFieldPeer
     */
    public void setEchoCharacter(char c) {
        setEchoChar(c);
    }

    void repaintText() {
        xtext.repaintNow();
    }

    public void setBackground(Color c) {
        if (log.isLoggable(Level.FINE)) log.fine("target="+ target + ", old=" + background + ", new=" + c);
        background = c;
        if (xtext != null) {
            xtext.setBackground(c);
            xtext.setSelectedTextColor(c);
        }
        repaintText();
    }

    public void setForeground(Color c) {
        foreground = c;
        if (xtext != null) {
            xtext.setForeground(foreground);
            xtext.setSelectionColor(foreground);
            xtext.setCaretColor(foreground);
        }
        repaintText();
    }

    public void setFont(Font f) {
        synchronized (getStateLock()) {
            font = f;
            if (xtext != null) {
                xtext.setFont(font);
            }
        }
        xtext.validate();
    }

    /**
     * DEPRECATED
     * @see java.awt.peer.TextFieldPeer
     */
    public Dimension preferredSize(int cols) {
        return getPreferredSize(cols);
    }

    /**
     * Deselects the the highlighted text.
     */
    public void deselect() {
        int selStart=xtext.getSelectionStart();
        int selEnd=xtext.getSelectionEnd();
        if (selStart != selEnd) {
            xtext.select(selStart,selStart);
        }
    }


    /**
     * to be implemented.
     * @see java.awt.peer.TextComponentPeer
     */
    public int getCaretPosition() {
        return xtext.getCaretPosition();
    }



    /**
     * @see java.awt.peer.TextComponentPeer
     */
    public void select(int s, int e) {
        xtext.select(s,e);
        // Fixed 5100806
        // We must take care that Swing components repainted correctly
        xtext.repaint();
    }


    public Dimension getMinimumSize() {
        return xtext.getMinimumSize();
    }

    public Dimension getPreferredSize() {
        return xtext.getPreferredSize();
    }

    public Dimension getPreferredSize(int cols) {
        return getMinimumSize(cols);
    }

    private static final int PADDING = 16;

    public Dimension getMinimumSize(int cols) {
        Font f = xtext.getFont();
        FontMetrics fm = xtext.getFontMetrics(f);
        return new Dimension(fm.charWidth('0') * cols + 10,
                             fm.getMaxDescent() + fm.getMaxAscent() + PADDING);

    }

    public boolean isFocusable() {
        return true;
    }

    // NOTE: This method is called by privileged threads.
    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
    public void action(final long when, final int modifiers) {
        postEvent(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
                                  text, when,
                                  modifiers));
    }


    protected void disposeImpl() {
    }


    public void repaint() {
        if (xtext  != null) xtext.repaint();
    }

    public void paint(Graphics g) {
        if (xtext  != null) xtext.paint(g);
    }


    public void print(Graphics g) {
        if (xtext != null) {
            xtext.print(g);
        }
    }

    public void focusLost(FocusEvent e) {
        super.focusLost(e);
        xtext.forwardFocusLost(e);
    }

    public void focusGained(FocusEvent e) {
        super.focusGained(e);
        xtext.forwardFocusGained(e);
    }

    void handleJavaKeyEvent(KeyEvent e) {
        ComponentAccessor.processEvent(xtext,e);
    }


    public void handleJavaMouseEvent( MouseEvent mouseEvent ) {
        super.handleJavaMouseEvent(mouseEvent);
        if (xtext != null)  {
            mouseEvent.setSource(xtext);
            int id = mouseEvent.getID();
            if (id == MouseEvent.MOUSE_DRAGGED || id == MouseEvent.MOUSE_MOVED)
                xtext.processMouseMotionEventImpl(mouseEvent);
            else
                xtext.processMouseEventImpl(mouseEvent);
        }
    }


    /**
     * DEPRECATED
     */
    public Dimension minimumSize() {
        return getMinimumSize();
    }

    /**
     * DEPRECATED
     */
    public Dimension minimumSize(int cols) {
        return getMinimumSize(cols);
    }

    public void setVisible(boolean b) {
        super.setVisible(b);
        if (xtext != null) xtext.setVisible(b);
    }

    public void setBounds(int x, int y, int width, int height, int op) {
        super.setBounds(x, y, width, height, op);
        if (xtext != null) {
            /*
             * Fixed 6277332, 6198290:
             * the coordinates is coming (to peer): relatively to closest HW parent
             * the coordinates is setting (to textField): relatively to closest ANY parent
             * the parent of peer is target.getParent()
             * the parent of textField 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();
            }
            xtext.setBounds(childX,childY,width,height);
            xtext.validate();
        }
    }


    //
    // Accessibility support
    //

    // stub functions: to be fully implemented in a future release
    public int getIndexAtPoint(int x, int y) { return -1; }
    public Rectangle getCharacterBounds(int i) { return null; }
    public long filterEvents(long mask) { return 0; }


    /*  To be fully implemented in a future release

        int oldSelectionStart;
        int oldSelectionEnd;

        public native int getIndexAtPoint(int x, int y);
        public native Rectangle getCharacterBounds(int i);
        public native long filterEvents(long mask);

        /**
         * Handle a change in the text selection endpoints
         * (Note: could be simply a change in the caret location)
         *
         public void selectionValuesChanged(int start, int end) {
         return;  // Need to write implemetation of this.
         }
    */


    class  AWTTextFieldUI extends MotifPasswordFieldUI {

        /**
         * Creates a UI for a JTextField.
         *
         * @param c the text field
         * @return the UI
         */
        JTextField jtf;


        protected String getPropertyPrefix() {
            JTextComponent comp = getComponent();
            if (comp instanceof JPasswordField && ((JPasswordField)comp).echoCharIsSet()) {
                return "PasswordField";
            } else {
                return "TextField";
            }
        }

        public void installUI(JComponent c) {
            super.installUI(c);

            jtf = (JTextField) c;

            JTextField editor = jtf;

            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 = editor.getBorder();
            if ((b == null) || (b instanceof UIResource)) {
                editor.setBorder(uidefaults.getBorder(prefix + ".border"));
            }

            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();
        }
    }

    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 XAWTTextField extends JPasswordField
        implements ActionListener,
                   DocumentListener
    {

        boolean isFocused = false;

        XComponentPeer peer;

        public XAWTTextField(String text, XComponentPeer peer, Container parent) {
            super(text);
            this.peer = peer;
            setDoubleBuffered(true);
            setFocusable(false);
            ComponentAccessor.setParent(this,parent);
            setBackground(peer.getPeerBackground());
            setForeground(peer.getPeerForeground());
            setFont(peer.getPeerFont());
            setCaretPosition(0);
            addActionListener(this);
            addNotify();

        }

        public void actionPerformed( ActionEvent actionEvent ) {
            peer.postEvent(new ActionEvent(peer.target,
                                           ActionEvent.ACTION_PERFORMED,
                                           getText(),
                                           actionEvent.getWhen(),
                                           actionEvent.getModifiers()));

        }

        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));
            }
        }

        public ComponentPeer getPeer() {
            return (ComponentPeer) peer;
        }


        public void repaintNow() {
            paintImmediately(getBounds());
        }

        public Graphics getGraphics() {
            return peer.getGraphics();
        }

        public void updateUI() {
            ComponentUI ui = new AWTTextFieldUI();
            setUI(ui);
        }


        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 processInputMethodEventImpl(InputMethodEvent e) {
            processInputMethodEvent(e);
        }

        public void processMouseEventImpl(MouseEvent e) {
            processMouseEvent(e);
        }

        public void processMouseMotionEventImpl(MouseEvent e) {
            processMouseMotionEvent(e);
        }

        // Fix for 4915454 - override the default implementation to avoid
        // loading SystemFlavorMap and associated classes.
        public void setTransferHandler(TransferHandler newHandler) {
            TransferHandler oldHandler = (TransferHandler)
                getClientProperty(XTextTransferHelper.getTransferHandlerKey());
            putClientProperty(XTextTransferHelper.getTransferHandlerKey(),
                              newHandler);

            firePropertyChange("transferHandler", oldHandler, newHandler);
        }

        public void setEchoChar(char c) {
            super.setEchoChar(c);
            ((AWTTextFieldUI)ui).installKeyboardActions();
        }
    }
}