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