1 /*
   2  * Copyright (c) 2003, 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 package sun.awt.X11;
  27 
  28 import java.awt.*;
  29 import java.awt.peer.*;
  30 import java.awt.event.*;
  31 import java.awt.event.ActionEvent;
  32 import java.awt.event.ActionListener;
  33 import java.awt.event.TextEvent;
  34 import javax.swing.text.*;
  35 import javax.swing.event.DocumentListener;
  36 import javax.swing.event.DocumentEvent;
  37 import javax.swing.plaf.ComponentUI;
  38 import javax.swing.InputMap;
  39 import javax.swing.JPasswordField;
  40 import javax.swing.SwingUtilities;
  41 import javax.swing.TransferHandler;
  42 
  43 import java.awt.event.MouseEvent;
  44 import java.awt.event.FocusEvent;
  45 import java.awt.event.KeyEvent;
  46 
  47 import javax.swing.plaf.UIResource;
  48 import javax.swing.UIDefaults;
  49 import javax.swing.JTextField;
  50 import javax.swing.JComponent;
  51 import javax.swing.border.Border;
  52 import com.sun.java.swing.plaf.motif.*;
  53 import java.awt.im.InputMethodRequests;
  54 
  55 import sun.util.logging.PlatformLogger;
  56 
  57 import sun.awt.CausedFocusEvent;
  58 import sun.awt.AWTAccessor;
  59 
  60 final class XTextFieldPeer extends XComponentPeer implements TextFieldPeer {
  61     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XTextField");
  62 
  63     private String text;
  64     private final XAWTTextField xtext;
  65     private final boolean firstChangeSkipped;
  66 
  67     XTextFieldPeer(TextField target) {
  68         super(target);
  69         text = target.getText();
  70         xtext = new XAWTTextField(text,this, target.getParent());
  71         xtext.getDocument().addDocumentListener(xtext);
  72         xtext.setCursor(target.getCursor());
  73         XToolkit.specialPeerMap.put(xtext,this);
  74 
  75         initTextField();
  76         setText(target.getText());
  77         if (target.echoCharIsSet()) {
  78             setEchoChar(target.getEchoChar());
  79         }
  80         else setEchoChar((char)0);
  81 
  82         int start = target.getSelectionStart();
  83         int end = target.getSelectionEnd();
  84         // Fix for 5100200
  85         // Restoring Motif behaviour
  86         // Since the end position of the selected text can be greater then the length of the text,
  87         // so we should set caret to max position of the text
  88         setCaretPosition(Math.min(end, text.length()));
  89         if (end > start) {
  90             select(start, end);
  91         }
  92 
  93         setEditable(target.isEditable());
  94 
  95         // After this line we should not change the component's text
  96         firstChangeSkipped = true;
  97     }
  98 
  99     public void dispose() {
 100         XToolkit.specialPeerMap.remove(xtext);
 101         // visible caret has a timer thread which must be stopped
 102         xtext.getCaret().setVisible(false);
 103         xtext.removeNotify();
 104         super.dispose();
 105     }
 106 
 107     void initTextField() {
 108         setVisible(target.isVisible());
 109 
 110         setBounds(x, y, width, height, SET_BOUNDS);
 111 
 112         AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
 113         foreground = compAccessor.getForeground(target);
 114         if (foreground == null)
 115             foreground = SystemColor.textText;
 116 
 117         setForeground(foreground);
 118 
 119         background = compAccessor.getBackground(target);
 120         if (background == null) {
 121             if (((TextField)target).isEditable()) background = SystemColor.text;
 122             else background = SystemColor.control;
 123         }
 124         setBackground(background);
 125 
 126         if (!target.isBackgroundSet()) {
 127             // This is a way to set the background color of the TextArea
 128             // without calling setBackground - go through accessor
 129             compAccessor.setBackground(target, background);
 130         }
 131         if (!target.isForegroundSet()) {
 132             target.setForeground(SystemColor.textText);
 133         }
 134 
 135         setFont(font);
 136     }
 137 
 138 
 139     /**
 140      * @see java.awt.peer.TextComponentPeer
 141      */
 142     public void setEditable(boolean editable) {
 143         if (xtext != null) {
 144             xtext.setEditable(editable);
 145             xtext.repaint();
 146         }
 147     }
 148 
 149     /**
 150      * @see java.awt.peer.ComponentPeer
 151      */
 152     public void setEnabled(boolean enabled) {
 153         super.setEnabled(enabled);
 154         if (xtext != null) {
 155             xtext.setEnabled(enabled);
 156             xtext.repaint();
 157         }
 158     }
 159 
 160     /**
 161      * @see java.awt.peer.TextComponentPeer
 162      */
 163 
 164     public InputMethodRequests getInputMethodRequests() {
 165         if (xtext != null) return xtext.getInputMethodRequests();
 166         else  return null;
 167 
 168     }
 169 
 170     void handleJavaInputMethodEvent(InputMethodEvent e) {
 171         if (xtext != null)
 172             xtext.processInputMethodEventImpl(e);
 173     }
 174 
 175 
 176     /**
 177      * @see java.awt.peer.TextFieldPeer
 178      */
 179     public void setEchoChar(char c) {
 180         if (xtext != null) {
 181             xtext.setEchoChar(c);
 182             xtext.putClientProperty("JPasswordField.cutCopyAllowed",
 183                     xtext.echoCharIsSet() ? Boolean.FALSE : Boolean.TRUE);
 184         }
 185     }
 186 
 187     /**
 188      * @see java.awt.peer.TextComponentPeer
 189      */
 190     public int getSelectionStart() {
 191         return xtext.getSelectionStart();
 192     }
 193 
 194     /**
 195      * @see java.awt.peer.TextComponentPeer
 196      */
 197     public int getSelectionEnd() {
 198         return xtext.getSelectionEnd();
 199     }
 200 
 201     /**
 202      * @see java.awt.peer.TextComponentPeer
 203      */
 204     public String getText() {
 205         return xtext.getText();
 206     }
 207 
 208     /**
 209      * @see java.awt.peer.TextComponentPeer
 210      */
 211     public void setText(String txt) {
 212         setXAWTTextField(txt);
 213         repaint();
 214     }
 215 
 216     private boolean setXAWTTextField(String txt) {
 217         text = txt;
 218         if (xtext != null)  {
 219             // JTextField.setText() posts two different events (remove & insert).
 220             // Since we make no differences between text events,
 221             // the document listener has to be disabled while
 222             // JTextField.setText() is called.
 223             xtext.getDocument().removeDocumentListener(xtext);
 224             xtext.setText(txt);
 225             if (firstChangeSkipped) {
 226                 postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED));
 227             }
 228             xtext.getDocument().addDocumentListener(xtext);
 229             xtext.setCaretPosition(0);
 230         }
 231         return true;
 232     }
 233 
 234     /**
 235      * to be implemented.
 236      * @see java.awt.peer.TextComponentPeer
 237      */
 238     public void setCaretPosition(int position) {
 239         if (xtext != null) xtext.setCaretPosition(position);
 240     }
 241 
 242     /**
 243      * DEPRECATED
 244      * @see java.awt.peer.TextFieldPeer
 245      */
 246     public void setEchoCharacter(char c) {
 247         setEchoChar(c);
 248     }
 249 
 250     void repaintText() {
 251         xtext.repaintNow();
 252     }
 253 
 254     public void setBackground(Color c) {
 255         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 256             log.fine("target="+ target + ", old=" + background + ", new=" + c);
 257         }
 258         background = c;
 259         if (xtext != null) {
 260             xtext.setBackground(c);
 261             xtext.setSelectedTextColor(c);
 262         }
 263         repaintText();
 264     }
 265 
 266     public void setForeground(Color c) {
 267         foreground = c;
 268         if (xtext != null) {
 269             xtext.setForeground(foreground);
 270             xtext.setSelectionColor(foreground);
 271             xtext.setCaretColor(foreground);
 272         }
 273         repaintText();
 274     }
 275 
 276     public void setFont(Font f) {
 277         synchronized (getStateLock()) {
 278             font = f;
 279             if (xtext != null) {
 280                 xtext.setFont(font);
 281             }
 282         }
 283         xtext.validate();
 284     }
 285 
 286     /**
 287      * DEPRECATED
 288      * @see java.awt.peer.TextFieldPeer
 289      */
 290     public Dimension preferredSize(int cols) {
 291         return getPreferredSize(cols);
 292     }
 293 
 294     /**
 295      * Deselects the the highlighted text.
 296      */
 297     public void deselect() {
 298         int selStart=xtext.getSelectionStart();
 299         int selEnd=xtext.getSelectionEnd();
 300         if (selStart != selEnd) {
 301             xtext.select(selStart,selStart);
 302         }
 303     }
 304 
 305 
 306     /**
 307      * to be implemented.
 308      * @see java.awt.peer.TextComponentPeer
 309      */
 310     public int getCaretPosition() {
 311         return xtext.getCaretPosition();
 312     }
 313 
 314 
 315 
 316     /**
 317      * @see java.awt.peer.TextComponentPeer
 318      */
 319     public void select(int s, int e) {
 320         xtext.select(s,e);
 321         // Fixed 5100806
 322         // We must take care that Swing components repainted correctly
 323         xtext.repaint();
 324     }
 325 
 326 
 327     public Dimension getMinimumSize() {
 328         return xtext.getMinimumSize();
 329     }
 330 
 331     public Dimension getPreferredSize() {
 332         return xtext.getPreferredSize();
 333     }
 334 
 335     public Dimension getPreferredSize(int cols) {
 336         return getMinimumSize(cols);
 337     }
 338 
 339     private static final int PADDING = 16;
 340 
 341     public Dimension getMinimumSize(int cols) {
 342         Font f = xtext.getFont();
 343         FontMetrics fm = xtext.getFontMetrics(f);
 344         return new Dimension(fm.charWidth('0') * cols + 10,
 345                              fm.getMaxDescent() + fm.getMaxAscent() + PADDING);
 346 
 347     }
 348 
 349     public boolean isFocusable() {
 350         return true;
 351     }
 352 
 353     // NOTE: This method is called by privileged threads.
 354     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 355     public void action(final long when, final int modifiers) {
 356         postEvent(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
 357                                   text, when,
 358                                   modifiers));
 359     }
 360 
 361 
 362     protected void disposeImpl() {
 363     }
 364 
 365 
 366     public void repaint() {
 367         if (xtext  != null) xtext.repaint();
 368     }
 369     @Override
 370     void paintPeer(final Graphics g) {
 371         if (xtext  != null) xtext.paint(g);
 372     }
 373 
 374     public void print(Graphics g) {
 375         if (xtext != null) {
 376             xtext.print(g);
 377         }
 378     }
 379 
 380     public void focusLost(FocusEvent e) {
 381         super.focusLost(e);
 382         xtext.forwardFocusLost(e);
 383     }
 384 
 385     public void focusGained(FocusEvent e) {
 386         super.focusGained(e);
 387         xtext.forwardFocusGained(e);
 388     }
 389 
 390     void handleJavaKeyEvent(KeyEvent e) {
 391         AWTAccessor.getComponentAccessor().processEvent(xtext,e);
 392     }
 393 
 394 
 395     public void handleJavaMouseEvent( MouseEvent mouseEvent ) {
 396         super.handleJavaMouseEvent(mouseEvent);
 397         if (xtext != null)  {
 398             mouseEvent.setSource(xtext);
 399             int id = mouseEvent.getID();
 400             if (id == MouseEvent.MOUSE_DRAGGED || id == MouseEvent.MOUSE_MOVED)
 401                 xtext.processMouseMotionEventImpl(mouseEvent);
 402             else
 403                 xtext.processMouseEventImpl(mouseEvent);
 404         }
 405     }
 406 
 407 
 408     /**
 409      * DEPRECATED
 410      */
 411     public Dimension minimumSize() {
 412         return getMinimumSize();
 413     }
 414 
 415     /**
 416      * DEPRECATED
 417      */
 418     public Dimension minimumSize(int cols) {
 419         return getMinimumSize(cols);
 420     }
 421 
 422     public void setVisible(boolean b) {
 423         super.setVisible(b);
 424         if (xtext != null) xtext.setVisible(b);
 425     }
 426 
 427     public void setBounds(int x, int y, int width, int height, int op) {
 428         super.setBounds(x, y, width, height, op);
 429         if (xtext != null) {
 430             /*
 431              * Fixed 6277332, 6198290:
 432              * the coordinates is coming (to peer): relatively to closest HW parent
 433              * the coordinates is setting (to textField): relatively to closest ANY parent
 434              * the parent of peer is target.getParent()
 435              * the parent of textField is the same
 436              * see 6277332, 6198290 for more information
 437              */
 438             int childX = x;
 439             int childY = y;
 440             Component parent = target.getParent();
 441             // we up to heavyweight parent in order to be sure
 442             // that the coordinates of the text pane is relatively to closest parent
 443             while (parent.isLightweight()){
 444                 childX -= parent.getX();
 445                 childY -= parent.getY();
 446                 parent = parent.getParent();
 447             }
 448             xtext.setBounds(childX,childY,width,height);
 449             xtext.validate();
 450         }
 451     }
 452 
 453 
 454     //
 455     // Accessibility support
 456     //
 457 
 458     // stub functions: to be fully implemented in a future release
 459     public int getIndexAtPoint(int x, int y) { return -1; }
 460     public Rectangle getCharacterBounds(int i) { return null; }
 461     public long filterEvents(long mask) { return 0; }
 462 
 463 
 464     /*  To be fully implemented in a future release
 465 
 466         int oldSelectionStart;
 467         int oldSelectionEnd;
 468 
 469         public native int getIndexAtPoint(int x, int y);
 470         public native Rectangle getCharacterBounds(int i);
 471         public native long filterEvents(long mask);
 472 
 473         /**
 474          * Handle a change in the text selection endpoints
 475          * (Note: could be simply a change in the caret location)
 476          *
 477          public void selectionValuesChanged(int start, int end) {
 478          return;  // Need to write implemetation of this.
 479          }
 480     */
 481 
 482 
 483     class  AWTTextFieldUI extends MotifPasswordFieldUI {
 484 
 485         /**
 486          * Creates a UI for a JTextField.
 487          *
 488          * @param c the text field
 489          * @return the UI
 490          */
 491         JTextField jtf;
 492 
 493 
 494         protected String getPropertyPrefix() {
 495             JTextComponent comp = getComponent();
 496             if (comp instanceof JPasswordField && ((JPasswordField)comp).echoCharIsSet()) {
 497                 return "PasswordField";
 498             } else {
 499                 return "TextField";
 500             }
 501         }
 502 
 503         public void installUI(JComponent c) {
 504             super.installUI(c);
 505 
 506             jtf = (JTextField) c;
 507 
 508             JTextField editor = jtf;
 509 
 510             UIDefaults uidefaults = XToolkit.getUIDefaults();
 511 
 512             String prefix = getPropertyPrefix();
 513             Font f = editor.getFont();
 514             if ((f == null) || (f instanceof UIResource)) {
 515                 editor.setFont(uidefaults.getFont(prefix + ".font"));
 516             }
 517 
 518             Color bg = editor.getBackground();
 519             if ((bg == null) || (bg instanceof UIResource)) {
 520                 editor.setBackground(uidefaults.getColor(prefix + ".background"));
 521             }
 522 
 523             Color fg = editor.getForeground();
 524             if ((fg == null) || (fg instanceof UIResource)) {
 525                 editor.setForeground(uidefaults.getColor(prefix + ".foreground"));
 526             }
 527 
 528             Color color = editor.getCaretColor();
 529             if ((color == null) || (color instanceof UIResource)) {
 530                 editor.setCaretColor(uidefaults.getColor(prefix + ".caretForeground"));
 531             }
 532 
 533             Color s = editor.getSelectionColor();
 534             if ((s == null) || (s instanceof UIResource)) {
 535                 editor.setSelectionColor(uidefaults.getColor(prefix + ".selectionBackground"));
 536             }
 537 
 538             Color sfg = editor.getSelectedTextColor();
 539             if ((sfg == null) || (sfg instanceof UIResource)) {
 540                 editor.setSelectedTextColor(uidefaults.getColor(prefix + ".selectionForeground"));
 541             }
 542 
 543             Color dfg = editor.getDisabledTextColor();
 544             if ((dfg == null) || (dfg instanceof UIResource)) {
 545                 editor.setDisabledTextColor(uidefaults.getColor(prefix + ".inactiveForeground"));
 546             }
 547 
 548             Border b = editor.getBorder();
 549             if ((b == null) || (b instanceof UIResource)) {
 550                 editor.setBorder(uidefaults.getBorder(prefix + ".border"));
 551             }
 552 
 553             Insets margin = editor.getMargin();
 554             if (margin == null || margin instanceof UIResource) {
 555                 editor.setMargin(uidefaults.getInsets(prefix + ".margin"));
 556             }
 557         }
 558 
 559         protected void installKeyboardActions() {
 560             super.installKeyboardActions();
 561 
 562             JTextComponent comp = getComponent();
 563 
 564             UIDefaults uidefaults = XToolkit.getUIDefaults();
 565 
 566             String prefix = getPropertyPrefix();
 567 
 568             InputMap map = (InputMap)uidefaults.get(prefix + ".focusInputMap");
 569 
 570             if (map != null) {
 571                 SwingUtilities.replaceUIInputMap(comp, JComponent.WHEN_FOCUSED,
 572                                                  map);
 573             }
 574         }
 575 
 576         protected Caret createCaret() {
 577             return new XTextAreaPeer.XAWTCaret();
 578         }
 579     }
 580 
 581     class XAWTTextField extends JPasswordField
 582         implements ActionListener,
 583                    DocumentListener
 584     {
 585 
 586         boolean isFocused = false;
 587 
 588         XComponentPeer peer;
 589 
 590         public XAWTTextField(String text, XComponentPeer peer, Container parent) {
 591             super(text);
 592             this.peer = peer;
 593             setDoubleBuffered(true);
 594             setFocusable(false);
 595             AWTAccessor.getComponentAccessor().setParent(this,parent);
 596             setBackground(peer.getPeerBackground());
 597             setForeground(peer.getPeerForeground());
 598             setFont(peer.getPeerFont());
 599             setCaretPosition(0);
 600             addActionListener(this);
 601             addNotify();
 602 
 603         }
 604 
 605         public void actionPerformed( ActionEvent actionEvent ) {
 606             peer.postEvent(new ActionEvent(peer.target,
 607                                            ActionEvent.ACTION_PERFORMED,
 608                                            getText(),
 609                                            actionEvent.getWhen(),
 610                                            actionEvent.getModifiers()));
 611 
 612         }
 613 
 614         public void insertUpdate(DocumentEvent e) {
 615             if (peer != null) {
 616                 peer.postEvent(new TextEvent(peer.target,
 617                                              TextEvent.TEXT_VALUE_CHANGED));
 618             }
 619         }
 620 
 621         public void removeUpdate(DocumentEvent e) {
 622             if (peer != null) {
 623                 peer.postEvent(new TextEvent(peer.target,
 624                                              TextEvent.TEXT_VALUE_CHANGED));
 625             }
 626         }
 627 
 628         public void changedUpdate(DocumentEvent e) {
 629             if (peer != null) {
 630                 peer.postEvent(new TextEvent(peer.target,
 631                                              TextEvent.TEXT_VALUE_CHANGED));
 632             }
 633         }
 634 
 635         public ComponentPeer getPeer() {
 636             return (ComponentPeer) peer;
 637         }
 638 
 639 
 640         public void repaintNow() {
 641             paintImmediately(getBounds());
 642         }
 643 
 644         public Graphics getGraphics() {
 645             return peer.getGraphics();
 646         }
 647 
 648         public void updateUI() {
 649             ComponentUI ui = new AWTTextFieldUI();
 650             setUI(ui);
 651         }
 652 
 653 
 654         void forwardFocusGained( FocusEvent e) {
 655             isFocused = true;
 656             FocusEvent fe = CausedFocusEvent.retarget(e, this);
 657             super.processFocusEvent(fe);
 658 
 659         }
 660 
 661 
 662         void forwardFocusLost( FocusEvent e) {
 663             isFocused = false;
 664             FocusEvent fe = CausedFocusEvent.retarget(e, this);
 665             super.processFocusEvent(fe);
 666 
 667         }
 668 
 669         public boolean hasFocus() {
 670             return isFocused;
 671         }
 672 
 673 
 674         public void processInputMethodEventImpl(InputMethodEvent e) {
 675             processInputMethodEvent(e);
 676         }
 677 
 678         public void processMouseEventImpl(MouseEvent e) {
 679             processMouseEvent(e);
 680         }
 681 
 682         public void processMouseMotionEventImpl(MouseEvent e) {
 683             processMouseMotionEvent(e);
 684         }
 685 
 686         // Fix for 4915454 - override the default implementation to avoid
 687         // loading SystemFlavorMap and associated classes.
 688         public void setTransferHandler(TransferHandler newHandler) {
 689             TransferHandler oldHandler = (TransferHandler)
 690                 getClientProperty(AWTAccessor.getClientPropertyKeyAccessor()
 691                                       .getJComponent_TRANSFER_HANDLER());
 692             putClientProperty(AWTAccessor.getClientPropertyKeyAccessor()
 693                                   .getJComponent_TRANSFER_HANDLER(),
 694                               newHandler);
 695 
 696             firePropertyChange("transferHandler", oldHandler, newHandler);
 697         }
 698 
 699         public void setEchoChar(char c) {
 700             super.setEchoChar(c);
 701             ((AWTTextFieldUI)ui).installKeyboardActions();
 702         }
 703     }
 704 }