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