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