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