1 /*
   2  * Copyright (c) 2003, 2015, 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             if (xtext.getBackground() != c) {
 263                 xtext.setBackground(c);
 264             }
 265             xtext.setSelectedTextColor(c);
 266         }
 267         repaintText();
 268     }
 269 
 270     @Override
 271     public void setForeground(Color c) {
 272         foreground = c;
 273         if (xtext != null) {
 274             if (xtext.getForeground() != c) {
 275                 xtext.setForeground(foreground);
 276             }
 277             xtext.setSelectionColor(foreground);
 278             xtext.setCaretColor(foreground);
 279         }
 280         repaintText();
 281     }
 282 
 283     @Override
 284     public void setFont(Font f) {
 285         synchronized (getStateLock()) {
 286             font = f;
 287             if (xtext != null && xtext.getFont() != f) {
 288                 xtext.setFont(font);
 289             }
 290         }
 291         xtext.validate();
 292     }
 293 
 294     /**
 295      * Deselects 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      * to be implemented.
 307      * @see java.awt.peer.TextComponentPeer
 308      */
 309     @Override
 310     public int getCaretPosition() {
 311         return xtext.getCaretPosition();
 312     }
 313 
 314     /**
 315      * @see java.awt.peer.TextComponentPeer
 316      */
 317     @Override
 318     public void select(int s, int e) {
 319         xtext.select(s,e);
 320         // Fixed 5100806
 321         // We must take care that Swing components repainted correctly
 322         xtext.repaint();
 323     }
 324 
 325     @Override
 326     public Dimension getMinimumSize() {
 327         return xtext.getMinimumSize();
 328     }
 329 
 330     @Override
 331     public Dimension getPreferredSize() {
 332         return xtext.getPreferredSize();
 333     }
 334 
 335     @Override
 336     public Dimension getPreferredSize(int cols) {
 337         return getMinimumSize(cols);
 338     }
 339 
 340     private static final int PADDING = 16;
 341 
 342     @Override
 343     public Dimension getMinimumSize(int cols) {
 344         Font f = xtext.getFont();
 345         FontMetrics fm = xtext.getFontMetrics(f);
 346         return new Dimension(fm.charWidth('0') * cols + 10,
 347                              fm.getMaxDescent() + fm.getMaxAscent() + PADDING);
 348     }
 349 
 350     @Override
 351     public boolean isFocusable() {
 352         return true;
 353     }
 354 
 355     // NOTE: This method is called by privileged threads.
 356     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 357     public void action(final long when, final int modifiers) {
 358         postEvent(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
 359                                   text, when,
 360                                   modifiers));
 361     }
 362 
 363     protected void disposeImpl() {
 364     }
 365 
 366     @Override
 367     public void repaint() {
 368         if (xtext  != null) xtext.repaint();
 369     }
 370     @Override
 371     void paintPeer(final Graphics g) {
 372         if (xtext  != null) xtext.paint(g);
 373     }
 374 
 375     @Override
 376     public void print(Graphics g) {
 377         if (xtext != null) {
 378             xtext.print(g);
 379         }
 380     }
 381 
 382     @Override
 383     public void focusLost(FocusEvent e) {
 384         super.focusLost(e);
 385         xtext.forwardFocusLost(e);
 386     }
 387 
 388     @Override
 389     public void focusGained(FocusEvent e) {
 390         super.focusGained(e);
 391         xtext.forwardFocusGained(e);
 392     }
 393 
 394     @Override
 395     void handleJavaKeyEvent(KeyEvent e) {
 396         AWTAccessor.getComponentAccessor().processEvent(xtext,e);
 397     }
 398 
 399 
 400     @Override
 401     public void handleJavaMouseEvent( MouseEvent mouseEvent ) {
 402         super.handleJavaMouseEvent(mouseEvent);
 403         if (xtext != null)  {
 404             mouseEvent.setSource(xtext);
 405             int id = mouseEvent.getID();
 406             if (id == MouseEvent.MOUSE_DRAGGED || id == MouseEvent.MOUSE_MOVED)
 407                 xtext.processMouseMotionEventImpl(mouseEvent);
 408             else
 409                 xtext.processMouseEventImpl(mouseEvent);
 410         }
 411     }
 412 
 413     /**
 414      * DEPRECATED
 415      */
 416     @Override
 417     public Dimension minimumSize() {
 418         return getMinimumSize();
 419     }
 420 
 421     @Override
 422     public void setVisible(boolean b) {
 423         super.setVisible(b);
 424         if (xtext != null) xtext.setVisible(b);
 425     }
 426 
 427     @Override
 428     public void setBounds(int x, int y, int width, int height, int op) {
 429         super.setBounds(x, y, width, height, op);
 430         if (xtext != null) {
 431             /*
 432              * Fixed 6277332, 6198290:
 433              * the coordinates is coming (to peer): relatively to closest HW parent
 434              * the coordinates is setting (to textField): relatively to closest ANY parent
 435              * the parent of peer is target.getParent()
 436              * the parent of textField is the same
 437              * see 6277332, 6198290 for more information
 438              */
 439             int childX = x;
 440             int childY = y;
 441             Component parent = target.getParent();
 442             // we up to heavyweight parent in order to be sure
 443             // that the coordinates of the text pane is relatively to closest parent
 444             while (parent.isLightweight()){
 445                 childX -= parent.getX();
 446                 childY -= parent.getY();
 447                 parent = parent.getParent();
 448             }
 449             xtext.setBounds(childX,childY,width,height);
 450             xtext.validate();
 451         }
 452     }
 453 
 454     final class AWTTextFieldUI extends MotifPasswordFieldUI {
 455 
 456         private JTextField jtf;
 457 
 458         @Override
 459         protected String getPropertyPrefix() {
 460             JTextComponent comp = getComponent();
 461             if (comp instanceof JPasswordField && ((JPasswordField)comp).echoCharIsSet()) {
 462                 return "PasswordField";
 463             } else {
 464                 return "TextField";
 465             }
 466         }
 467 
 468         @Override
 469         public void installUI(JComponent c) {
 470             super.installUI(c);
 471 
 472             jtf = (JTextField) c;
 473 
 474             JTextField editor = jtf;
 475 
 476             UIDefaults uidefaults = XToolkit.getUIDefaults();
 477 
 478             String prefix = getPropertyPrefix();
 479             Font f = editor.getFont();
 480             if ((f == null) || (f instanceof UIResource)) {
 481                 editor.setFont(uidefaults.getFont(prefix + ".font"));
 482             }
 483 
 484             Color bg = editor.getBackground();
 485             if ((bg == null) || (bg instanceof UIResource)) {
 486                 editor.setBackground(uidefaults.getColor(prefix + ".background"));
 487             }
 488 
 489             Color fg = editor.getForeground();
 490             if ((fg == null) || (fg instanceof UIResource)) {
 491                 editor.setForeground(uidefaults.getColor(prefix + ".foreground"));
 492             }
 493 
 494             Color color = editor.getCaretColor();
 495             if ((color == null) || (color instanceof UIResource)) {
 496                 editor.setCaretColor(uidefaults.getColor(prefix + ".caretForeground"));
 497             }
 498 
 499             Color s = editor.getSelectionColor();
 500             if ((s == null) || (s instanceof UIResource)) {
 501                 editor.setSelectionColor(uidefaults.getColor(prefix + ".selectionBackground"));
 502             }
 503 
 504             Color sfg = editor.getSelectedTextColor();
 505             if ((sfg == null) || (sfg instanceof UIResource)) {
 506                 editor.setSelectedTextColor(uidefaults.getColor(prefix + ".selectionForeground"));
 507             }
 508 
 509             Color dfg = editor.getDisabledTextColor();
 510             if ((dfg == null) || (dfg instanceof UIResource)) {
 511                 editor.setDisabledTextColor(uidefaults.getColor(prefix + ".inactiveForeground"));
 512             }
 513 
 514             Border b = editor.getBorder();
 515             if ((b == null) || (b instanceof UIResource)) {
 516                 editor.setBorder(uidefaults.getBorder(prefix + ".border"));
 517             }
 518 
 519             Insets margin = editor.getMargin();
 520             if (margin == null || margin instanceof UIResource) {
 521                 editor.setMargin(uidefaults.getInsets(prefix + ".margin"));
 522             }
 523         }
 524 
 525         @Override
 526         protected void installKeyboardActions() {
 527             super.installKeyboardActions();
 528 
 529             JTextComponent comp = getComponent();
 530 
 531             UIDefaults uidefaults = XToolkit.getUIDefaults();
 532 
 533             String prefix = getPropertyPrefix();
 534 
 535             InputMap map = (InputMap)uidefaults.get(prefix + ".focusInputMap");
 536 
 537             if (map != null) {
 538                 SwingUtilities.replaceUIInputMap(comp, JComponent.WHEN_FOCUSED,
 539                                                  map);
 540             }
 541         }
 542 
 543         @Override
 544         protected Caret createCaret() {
 545             return new XTextAreaPeer.XAWTCaret();
 546         }
 547     }
 548 
 549     @SuppressWarnings("serial") // JDK-implementation class
 550     final class XAWTTextField extends JPasswordField
 551             implements ActionListener, DocumentListener {
 552 
 553         private boolean isFocused = false;
 554         private final XComponentPeer xwin;
 555 
 556         XAWTTextField(String text, XComponentPeer xwin, Container parent) {
 557             super(text);
 558             this.xwin = xwin;
 559             setDoubleBuffered(true);
 560             setFocusable(false);
 561             AWTAccessor.getComponentAccessor().setParent(this,parent);
 562             setBackground(xwin.getPeerBackground());
 563             setForeground(xwin.getPeerForeground());
 564             setFont(xwin.getPeerFont());
 565             setCaretPosition(0);
 566             addActionListener(this);
 567             addNotify();
 568         }
 569 
 570         @Override
 571         @SuppressWarnings("deprecation")
 572         public void actionPerformed( ActionEvent actionEvent ) {
 573             xwin.postEvent(
 574                     new ActionEvent(xwin.target, ActionEvent.ACTION_PERFORMED,
 575                                     getText(), actionEvent.getWhen(),
 576                                     actionEvent.getModifiers()));
 577 
 578         }
 579 
 580         @Override
 581         public void insertUpdate(DocumentEvent e) {
 582             if (xwin != null) {
 583                 xwin.postEvent(new TextEvent(xwin.target,
 584                                              TextEvent.TEXT_VALUE_CHANGED));
 585             }
 586         }
 587 
 588         @Override
 589         public void removeUpdate(DocumentEvent e) {
 590             if (xwin != null) {
 591                 xwin.postEvent(new TextEvent(xwin.target,
 592                                              TextEvent.TEXT_VALUE_CHANGED));
 593             }
 594         }
 595 
 596         @Override
 597         public void changedUpdate(DocumentEvent e) {
 598             if (xwin != null) {
 599                 xwin.postEvent(new TextEvent(xwin.target,
 600                                              TextEvent.TEXT_VALUE_CHANGED));
 601             }
 602         }
 603 
 604         public void repaintNow() {
 605             paintImmediately(getBounds());
 606         }
 607 
 608         @Override
 609         public Graphics getGraphics() {
 610             return xwin.getGraphics();
 611         }
 612 
 613         @Override
 614         public void updateUI() {
 615             ComponentUI ui = new AWTTextFieldUI();
 616             setUI(ui);
 617         }
 618 
 619         void forwardFocusGained( FocusEvent e) {
 620             isFocused = true;
 621             FocusEvent fe = CausedFocusEvent.retarget(e, this);
 622             super.processFocusEvent(fe);
 623         }
 624 
 625         void forwardFocusLost( FocusEvent e) {
 626             isFocused = false;
 627             FocusEvent fe = CausedFocusEvent.retarget(e, this);
 628             super.processFocusEvent(fe);
 629 
 630         }
 631 
 632         @Override
 633         public boolean hasFocus() {
 634             return isFocused;
 635         }
 636 
 637         public void processInputMethodEventImpl(InputMethodEvent e) {
 638             processInputMethodEvent(e);
 639         }
 640 
 641         public void processMouseEventImpl(MouseEvent e) {
 642             processMouseEvent(e);
 643         }
 644 
 645         public void processMouseMotionEventImpl(MouseEvent e) {
 646             processMouseMotionEvent(e);
 647         }
 648 
 649         // Fix for 4915454 - override the default implementation to avoid
 650         // loading SystemFlavorMap and associated classes.
 651         @Override
 652         public void setTransferHandler(TransferHandler newHandler) {
 653             TransferHandler oldHandler = (TransferHandler)
 654                 getClientProperty(AWTAccessor.getClientPropertyKeyAccessor()
 655                                       .getJComponent_TRANSFER_HANDLER());
 656             putClientProperty(AWTAccessor.getClientPropertyKeyAccessor()
 657                                   .getJComponent_TRANSFER_HANDLER(),
 658                               newHandler);
 659 
 660             firePropertyChange("transferHandler", oldHandler, newHandler);
 661         }
 662 
 663         @Override
 664         public void setEchoChar(char c) {
 665             super.setEchoChar(c);
 666             ((AWTTextFieldUI)ui).installKeyboardActions();
 667         }
 668     }
 669 }