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.ComponentPeer;
30 import java.awt.peer.TextAreaPeer;
31 import java.awt.event.*;
32 import javax.swing.event.DocumentListener;
33 import javax.swing.event.DocumentEvent;
34 import javax.swing.JTextArea;
35 import javax.swing.JComponent;
36 import javax.swing.JScrollPane;
37 import javax.swing.JScrollBar;
38 import javax.swing.plaf.ComponentUI;
39 import com.sun.java.swing.plaf.motif.MotifTextAreaUI;
40 import javax.swing.plaf.UIResource;
41 import javax.swing.UIDefaults;
42 import javax.swing.border.Border;
43 import javax.swing.border.EmptyBorder;
44 import javax.swing.border.CompoundBorder;
45 import javax.swing.border.AbstractBorder;
46 import javax.swing.JButton;
47 import javax.swing.JViewport;
48 import javax.swing.InputMap;
49 import javax.swing.SwingUtilities;
50 import javax.swing.TransferHandler;
51 import javax.swing.plaf.basic.BasicArrowButton;
52 import javax.swing.plaf.basic.BasicScrollBarUI;
53 import javax.swing.plaf.basic.BasicScrollPaneUI;
54 import java.beans.PropertyChangeEvent;
55 import java.beans.PropertyChangeListener;
56 import javax.swing.text.Caret;
57 import javax.swing.text.DefaultCaret;
58 import javax.swing.text.JTextComponent;
59 
60 import javax.swing.plaf.BorderUIResource;
61 import java.awt.im.InputMethodRequests;
62 import sun.awt.AWTAccessor;
63 import sun.awt.SunToolkit;
64 
65 final class XTextAreaPeer extends XComponentPeer implements TextAreaPeer {
66 
67     private final AWTTextPane textPane;
68     private final AWTTextArea jtext;
69     private final boolean firstChangeSkipped;
70 
71     private final JavaMouseEventHandler javaMouseEventHandler =
72             new JavaMouseEventHandler(this);
73 
74     /**
75      * Create a Text area.
76      */
XTextAreaPeer(TextArea target)77     XTextAreaPeer(TextArea target) {
78         super(target);
79 
80         // some initializations require that target be set even
81         // though init(target) has not been called
82         this.target = target;
83 
84         //ComponentAccessor.enableEvents(target,AWTEvent.MOUSE_WHEEL_EVENT_MASK);
85 
86         String text = target.getText();
87         jtext = new AWTTextArea(text, this);
88         jtext.setWrapStyleWord(true);
89         jtext.getDocument().addDocumentListener(jtext);
90         XToolkit.specialPeerMap.put(jtext,this);
91         textPane = new AWTTextPane(jtext,this, target.getParent());
92 
93         setBounds(x, y, width, height, SET_BOUNDS);
94         textPane.setVisible(true);
95         textPane.validate();
96 
97         AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
98         foreground = compAccessor.getForeground(target);
99         if (foreground == null)  {
100             foreground = SystemColor.textText;
101         }
102         setForeground(foreground);
103 
104         background = compAccessor.getBackground(target);
105         if (background == null) {
106             if (target.isEditable()) background = SystemColor.text;
107             else background = SystemColor.control;
108         }
109         setBackground(background);
110 
111         if (!target.isBackgroundSet()) {
112             // This is a way to set the background color of the TextArea
113             // without calling setBackground - go through accessor
114             compAccessor.setBackground(target, background);
115         }
116         if (!target.isForegroundSet()) {
117             target.setForeground(SystemColor.textText);
118         }
119 
120         setFont(font);
121 
122         // set the text of this object to the text of its target
123         setTextImpl(target.getText());  //?? should this be setText
124 
125         int start = target.getSelectionStart();
126         int end = target.getSelectionEnd();
127         // Fix for 5100200
128         // Restoring Motif behaviour
129         // Since the end position of the selected text can be greater than the length of the text,
130         // so we should set caret to max position of the text
131         setCaretPosition(Math.min(end, text.length()));
132         if (end > start) {
133             // Should be called after setText() and setCaretPosition()
134             select(start, end);
135         }
136         setEditable(target.isEditable());
137         setScrollBarVisibility();
138         // After this line we should not change the component's text
139         firstChangeSkipped = true;
140         compAccessor.setPeer(textPane, this);
141     }
142 
143     @Override
dispose()144     public void dispose() {
145         XToolkit.specialPeerMap.remove(jtext);
146         // visible caret has a timer thread which must be stopped
147         jtext.getCaret().setVisible(false);
148         jtext.removeNotify();
149         super.dispose();
150     }
151 
152     /*
153      * The method overrides one from XComponentPeer
154      * If ignoreSubComponents=={@code true} it calls super.
155      * If ignoreSubComponents=={@code false} it uses the XTextArea machinery
156      * to change cursor appropriately. In particular it changes the cursor to
157      * default if over scrollbars.
158      */
159     @Override
pSetCursor(Cursor cursor, boolean ignoreSubComponents)160     public void pSetCursor(Cursor cursor, boolean ignoreSubComponents) {
161         if (ignoreSubComponents ||
162             javaMouseEventHandler == null) {
163             super.pSetCursor(cursor, true);
164             return;
165         }
166 
167         Point cursorPos = new Point();
168         ((XGlobalCursorManager)XGlobalCursorManager.getCursorManager()).getCursorPos(cursorPos);
169 
170         final Point onScreen = getLocationOnScreen();
171         Point localPoint = new Point(cursorPos.x - onScreen.x, cursorPos.y - onScreen.y );
172 
173         javaMouseEventHandler.setPointerToUnderPoint(localPoint);
174         javaMouseEventHandler.setCursor();
175     }
176 
setScrollBarVisibility()177     private void setScrollBarVisibility() {
178         int visibility = ((TextArea)target).getScrollbarVisibility();
179         jtext.setLineWrap(false);
180 
181         if (visibility == TextArea.SCROLLBARS_NONE) {
182             textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
183             textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
184             jtext.setLineWrap(true);
185         }
186         else if (visibility == TextArea.SCROLLBARS_BOTH) {
187 
188             textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
189             textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
190         }
191         else if (visibility == TextArea.SCROLLBARS_VERTICAL_ONLY) {
192             textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
193             textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
194             jtext.setLineWrap(true);
195         }
196         else if (visibility == TextArea.SCROLLBARS_HORIZONTAL_ONLY) {
197             textPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
198             textPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
199         }
200     }
201 
202     /**
203      * Compute minimum size.
204      */
205     @Override
getMinimumSize()206     public Dimension getMinimumSize() {
207         return getMinimumSize(10, 60);
208     }
209 
210     @Override
getPreferredSize(int rows, int cols)211     public Dimension getPreferredSize(int rows, int cols) {
212         return getMinimumSize(rows, cols);
213     }
214 
215     /**
216      * @see java.awt.peer.TextAreaPeer
217      */
218     @Override
getMinimumSize(int rows, int cols)219     public Dimension getMinimumSize(int rows, int cols) {
220         /*    Dimension d = null;
221               if (jtext != null) {
222               d = jtext.getMinimumSize(rows,cols);
223               }
224               return d;
225         */
226 
227         int vsbwidth=0;
228         int hsbheight=0;
229 
230         JScrollBar vsb = textPane.getVerticalScrollBar();
231         if (vsb != null) {
232             vsbwidth = vsb.getMinimumSize().width;
233         }
234 
235         JScrollBar hsb = textPane.getHorizontalScrollBar();
236         if (hsb != null) {
237             hsbheight = hsb.getMinimumSize().height;
238         }
239 
240         Font f = jtext.getFont();
241         FontMetrics fm = jtext.getFontMetrics(f);
242 
243         return new Dimension(fm.charWidth('0') * cols + /*2*XMARGIN +*/ vsbwidth,
244                              fm.getHeight() * rows + /*2*YMARGIN +*/ hsbheight);
245     }
246 
247     @Override
isFocusable()248     public boolean isFocusable() {
249         return true;
250     }
251 
252     @Override
setVisible(boolean b)253     public void setVisible(boolean b) {
254         super.setVisible(b);
255         if (textPane != null)
256             textPane.setVisible(b);
257     }
258 
repaintText()259     void repaintText() {
260         jtext.repaintNow();
261     }
262 
263     @Override
focusGained(FocusEvent e)264     public void focusGained(FocusEvent e) {
265         super.focusGained(e);
266         jtext.forwardFocusGained(e);
267     }
268 
269     @Override
focusLost(FocusEvent e)270     public void focusLost(FocusEvent e) {
271         super.focusLost(e);
272         jtext.forwardFocusLost(e);
273     }
274 
275     /**
276      * Paint the component
277      * this method is called when the repaint instruction has been used
278      */
279     @Override
repaint()280     public void repaint() {
281         if (textPane  != null)  {
282             //textPane.validate();
283             textPane.repaint();
284         }
285     }
286 
287     @Override
paintPeer(final Graphics g)288     void paintPeer(final Graphics g) {
289         if (textPane  != null)  {
290             textPane.paint(g);
291         }
292     }
293 
294     @Override
setBounds(int x, int y, int width, int height, int op)295     public void setBounds(int x, int y, int width, int height, int op) {
296         super.setBounds(x, y, width, height, op);
297         if (textPane != null) {
298             /*
299              * Fixed 6277332, 6198290:
300              * the coordinates is coming (to peer): relatively to closest HW parent
301              * the coordinates is setting (to textPane): relatively to closest ANY parent
302              * the parent of peer is target.getParent()
303              * the parent of textPane is the same
304              * see 6277332, 6198290 for more information
305              */
306             int childX = x;
307             int childY = y;
308             Component parent = target.getParent();
309             // we up to heavyweight parent in order to be sure
310             // that the coordinates of the text pane is relatively to closest parent
311             while (parent.isLightweight()){
312                 childX -= parent.getX();
313                 childY -= parent.getY();
314                 parent = parent.getParent();
315             }
316             textPane.setBounds(childX,childY,width,height);
317             textPane.validate();
318         }
319     }
320 
321     @Override
handleJavaKeyEvent(KeyEvent e)322     void handleJavaKeyEvent(KeyEvent e) {
323         AWTAccessor.getComponentAccessor().processEvent(jtext,e);
324     }
325 
326     @Override
handlesWheelScrolling()327     public boolean handlesWheelScrolling() { return true; }
328 
329     @Override
handleJavaMouseWheelEvent(MouseWheelEvent e)330     void handleJavaMouseWheelEvent(MouseWheelEvent e) {
331         AWTAccessor.getComponentAccessor().processEvent(textPane, e);
332     }
333 
334     @Override
handleJavaMouseEvent( MouseEvent e )335     public void handleJavaMouseEvent( MouseEvent e ) {
336         super.handleJavaMouseEvent( e );
337         javaMouseEventHandler.handle( e );
338     }
339 
340     @Override
handleJavaInputMethodEvent(InputMethodEvent e)341     void handleJavaInputMethodEvent(InputMethodEvent e) {
342         if (jtext != null)
343             jtext.processInputMethodEventPublic(e);
344     }
345 
346     /**
347      * @see java.awt.peer.TextComponentPeer
348      */
349     @Override
select(int s, int e)350     public void select(int s, int e) {
351         jtext.select(s, e);
352         // Fixed 5100806
353         // We must take care that Swing components repainted correctly
354         jtext.repaint();
355     }
356 
357     @Override
setBackground(Color c)358     public void setBackground(Color c) {
359         super.setBackground(c);
360 //          synchronized (getStateLock()) {
361 //              background = c;
362 //          }
363         if (jtext != null) {
364             jtext.setBackground(c);
365             jtext.setSelectedTextColor(c);
366         }
367 //          repaintText();
368     }
369 
370     @Override
setForeground(Color c)371     public void setForeground(Color c) {
372         super.setForeground(c);
373 //          synchronized (getStateLock()) {
374 //              foreground = c;
375 //          }
376         if (jtext != null) {
377             jtext.setForeground(foreground);
378             jtext.setSelectionColor(foreground);
379             jtext.setCaretColor(foreground);
380         }
381 //          repaintText();
382     }
383 
384     @Override
setFont(Font f)385     public void setFont(Font f) {
386         super.setFont(f);
387 //          synchronized (getStateLock()) {
388 //              font = f;
389 //          }
390         if (jtext != null) {
391             jtext.setFont(font);
392         }
393         textPane.validate();
394     }
395 
396     /**
397      * @see java.awt.peer.TextComponentPeer
398      */
399     @Override
setEditable(boolean editable)400     public void setEditable(boolean editable) {
401         if (jtext != null) jtext.setEditable(editable);
402         repaintText();
403     }
404 
405     /**
406      * @see java.awt.peer.ComponentPeer
407      */
408     @Override
setEnabled(boolean enabled)409     public void setEnabled(boolean enabled) {
410         super.setEnabled(enabled);
411         if (jtext != null) {
412             jtext.setEnabled(enabled);
413             jtext.repaint();
414         }
415     }
416 
417     /**
418      * @see java.awt.peer.TextComponentPeer
419      */
420     @Override
getInputMethodRequests()421     public InputMethodRequests getInputMethodRequests() {
422         if (jtext != null) return jtext.getInputMethodRequests();
423         else  return null;
424     }
425 
426     /**
427      * @see java.awt.peer.TextComponentPeer
428      */
429     @Override
getSelectionStart()430     public int getSelectionStart() {
431         return jtext.getSelectionStart();
432     }
433 
434     /**
435      * @see java.awt.peer.TextComponentPeer
436      */
437     @Override
getSelectionEnd()438     public int getSelectionEnd() {
439         return jtext.getSelectionEnd();
440     }
441 
442     /**
443      * @see java.awt.peer.TextComponentPeer
444      */
445     @Override
getText()446     public String getText() {
447         return jtext.getText();
448     }
449 
450     /**
451      * @see java.awt.peer.TextComponentPeer
452      */
453     @Override
setText(String text)454     public void setText(String text) {
455         setTextImpl(text);
456         repaintText();
457     }
458 
setTextImpl(String txt)459     private void setTextImpl(String txt) {
460         if (jtext != null) {
461             // JTextArea.setText() posts two different events (remove & insert).
462             // Since we make no differences between text events,
463             // the document listener has to be disabled while
464             // JTextArea.setText() is called.
465             jtext.getDocument().removeDocumentListener(jtext);
466             jtext.setText(txt);
467             if (firstChangeSkipped) {
468                 postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED));
469             }
470             jtext.getDocument().addDocumentListener(jtext);
471         }
472     }
473 
474     /**
475      * insert the text "txt on position "pos" in the array lines
476      * @see java.awt.peer.TextAreaPeer
477      */
478     @Override
insert(String txt, int p)479     public void insert(String txt, int p) {
480         if (jtext != null) {
481             boolean doScroll = (p >= jtext.getDocument().getLength() && jtext.getDocument().getLength() != 0);
482             jtext.insert(txt,p);
483             textPane.validate();
484             if (doScroll) {
485                 JScrollBar bar = textPane.getVerticalScrollBar();
486                 if (bar != null) {
487                     bar.setValue(bar.getMaximum()-bar.getVisibleAmount());
488                 }
489             }
490         }
491     }
492 
493     /**
494      * replace the text between the position "s" and "e" with "txt"
495      * @see java.awt.peer.TextAreaPeer
496      */
497     @Override
replaceRange(String txt, int s, int e)498     public void replaceRange(String txt, int s, int e) {
499         if (jtext != null) {
500             // JTextArea.replaceRange() posts two different events.
501             // Since we make no differences between text events,
502             // the document listener has to be disabled while
503             // JTextArea.replaceRange() is called.
504             jtext.getDocument().removeDocumentListener(jtext);
505             jtext.replaceRange(txt, s, e);
506             postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED));
507             jtext.getDocument().addDocumentListener(jtext);
508         }
509     }
510 
511     /**
512      * to be implemented.
513      * @see java.awt.peer.TextComponentPeer
514      */
515     @Override
setCaretPosition(int position)516     public void setCaretPosition(int position) {
517         jtext.setCaretPosition(position);
518     }
519 
520     /**
521      * to be implemented.
522      * @see java.awt.peer.TextComponentPeer
523      */
524     @Override
getCaretPosition()525     public int getCaretPosition() {
526         return jtext.getCaretPosition();
527     }
528 
529     final class AWTTextAreaUI extends MotifTextAreaUI {
530 
531         private JTextArea jta;
532 
533         @Override
getPropertyPrefix()534         protected String getPropertyPrefix() { return "TextArea"; }
535 
536         @Override
installUI(JComponent c)537         public void installUI(JComponent c) {
538             super.installUI(c);
539 
540             jta = (JTextArea) c;
541 
542             JTextArea editor = jta;
543 
544             UIDefaults uidefaults = XToolkit.getUIDefaults();
545 
546             String prefix = getPropertyPrefix();
547             Font f = editor.getFont();
548             if ((f == null) || (f instanceof UIResource)) {
549                 editor.setFont(uidefaults.getFont(prefix + ".font"));
550             }
551 
552             Color bg = editor.getBackground();
553             if ((bg == null) || (bg instanceof UIResource)) {
554                 editor.setBackground(uidefaults.getColor(prefix + ".background"));
555             }
556 
557             Color fg = editor.getForeground();
558             if ((fg == null) || (fg instanceof UIResource)) {
559                 editor.setForeground(uidefaults.getColor(prefix + ".foreground"));
560             }
561 
562             Color color = editor.getCaretColor();
563             if ((color == null) || (color instanceof UIResource)) {
564                 editor.setCaretColor(uidefaults.getColor(prefix + ".caretForeground"));
565             }
566 
567             Color s = editor.getSelectionColor();
568             if ((s == null) || (s instanceof UIResource)) {
569                 editor.setSelectionColor(uidefaults.getColor(prefix + ".selectionBackground"));
570             }
571 
572             Color sfg = editor.getSelectedTextColor();
573             if ((sfg == null) || (sfg instanceof UIResource)) {
574                 editor.setSelectedTextColor(uidefaults.getColor(prefix + ".selectionForeground"));
575             }
576 
577             Color dfg = editor.getDisabledTextColor();
578             if ((dfg == null) || (dfg instanceof UIResource)) {
579                 editor.setDisabledTextColor(uidefaults.getColor(prefix + ".inactiveForeground"));
580             }
581 
582             Border b = new BevelBorder(false,SystemColor.controlDkShadow,SystemColor.controlLtHighlight);
583             editor.setBorder(new BorderUIResource.CompoundBorderUIResource(
584                 b,new EmptyBorder(2, 2, 2, 2)));
585 
586             Insets margin = editor.getMargin();
587             if (margin == null || margin instanceof UIResource) {
588                 editor.setMargin(uidefaults.getInsets(prefix + ".margin"));
589             }
590         }
591 
592         @Override
installKeyboardActions()593         protected void installKeyboardActions() {
594             super.installKeyboardActions();
595 
596             JTextComponent comp = getComponent();
597 
598             UIDefaults uidefaults = XToolkit.getUIDefaults();
599 
600             String prefix = getPropertyPrefix();
601 
602             InputMap map = (InputMap)uidefaults.get(prefix + ".focusInputMap");
603 
604             if (map != null) {
605                 SwingUtilities.replaceUIInputMap(comp, JComponent.WHEN_FOCUSED,
606                                                  map);
607             }
608         }
609 
610         @Override
createCaret()611         protected Caret createCaret() {
612             return new XAWTCaret();
613         }
614     }
615 
616     @SuppressWarnings("serial") // JDK-implementation class
617     static final class XAWTCaret extends DefaultCaret {
618         @Override
focusGained(FocusEvent e)619         public void focusGained(FocusEvent e) {
620             super.focusGained(e);
621             if (getComponent().isEnabled()){
622                 // Make sure the cursor is visible in case of non-editable TextArea
623                 super.setVisible(true);
624             }
625             getComponent().repaint();
626         }
627 
628         @Override
focusLost(FocusEvent e)629         public void focusLost(FocusEvent e) {
630             super.focusLost(e);
631             getComponent().repaint();
632         }
633     }
634 
635     @SuppressWarnings("serial") // JDK-implementation class
636     final class XAWTScrollBarButton extends BasicArrowButton {
637 
638         private UIDefaults uidefaults = XToolkit.getUIDefaults();
639         private Color darkShadow = SystemColor.controlShadow;
640         private Color lightShadow = SystemColor.controlLtHighlight;
641         private Color buttonBack = uidefaults.getColor("ScrollBar.track");
642 
XAWTScrollBarButton(int direction)643         XAWTScrollBarButton(int direction) {
644             super(direction);
645 
646             switch (direction) {
647             case NORTH:
648             case SOUTH:
649             case EAST:
650             case WEST:
651                 this.direction = direction;
652                 break;
653             default:
654                 throw new IllegalArgumentException("invalid direction");
655             }
656 
657             setRequestFocusEnabled(false);
658             setOpaque(true);
659             setBackground(uidefaults.getColor("ScrollBar.thumb"));
660             setForeground(uidefaults.getColor("ScrollBar.foreground"));
661         }
662 
663         @Override
getPreferredSize()664         public Dimension getPreferredSize() {
665             switch (direction) {
666             case NORTH:
667             case SOUTH:
668                 return new Dimension(11, 12);
669             case EAST:
670             case WEST:
671             default:
672                 return new Dimension(12, 11);
673             }
674         }
675 
676         @Override
getMinimumSize()677         public Dimension getMinimumSize() {
678             return getPreferredSize();
679         }
680 
681         @Override
getMaximumSize()682         public Dimension getMaximumSize() {
683             return getPreferredSize();
684         }
685 
686         @Override
isFocusTraversable()687         public boolean isFocusTraversable() {
688             return false;
689         }
690 
691         @Override
paint(Graphics g)692         public void paint(Graphics g)
693         {
694             int w = getWidth();
695             int h = getHeight();
696 
697             if (isOpaque()) {
698                 g.setColor(buttonBack);
699                 g.fillRect(0, 0, w, h);
700             }
701 
702             boolean isPressed = getModel().isPressed();
703             Color lead = (isPressed) ? darkShadow : lightShadow;
704             Color trail = (isPressed) ? lightShadow : darkShadow;
705             Color fill = getBackground();
706 
707             int cx = w / 2;
708             int cy = h / 2;
709             int s = Math.min(w, h);
710 
711             switch (direction) {
712             case NORTH:
713                 g.setColor(lead);
714                 g.drawLine(cx, 0, cx, 0);
715                 for (int x = cx - 1, y = 1, dx = 1; y <= s - 2; y += 2) {
716                     g.setColor(lead);
717                     g.drawLine(x, y, x, y);
718                     if (y >= (s - 2)) {
719                         g.drawLine(x, y + 1, x, y + 1);
720                     }
721                     g.setColor(fill);
722                     g.drawLine(x + 1, y, x + dx, y);
723                     if (y < (s - 2)) {
724                         g.drawLine(x, y + 1, x + dx + 1, y + 1);
725                     }
726                     g.setColor(trail);
727                     g.drawLine(x + dx + 1, y, x + dx + 1, y);
728                     if (y >= (s - 2)) {
729                         g.drawLine(x + 1, y + 1, x + dx + 1, y + 1);
730                     }
731                     dx += 2;
732                     x -= 1;
733                 }
734                 break;
735 
736             case SOUTH:
737                 g.setColor(trail);
738                 g.drawLine(cx, s, cx, s);
739                 for (int x = cx - 1, y = s - 1, dx = 1; y >= 1; y -= 2) {
740                     g.setColor(lead);
741                     g.drawLine(x, y, x, y);
742                     if (y <= 2) {
743                         g.drawLine(x, y - 1, x + dx + 1, y - 1);
744                     }
745                     g.setColor(fill);
746                     g.drawLine(x + 1, y, x + dx, y);
747                     if (y > 2) {
748                         g.drawLine(x, y - 1, x + dx + 1, y - 1);
749                     }
750                     g.setColor(trail);
751                     g.drawLine(x + dx + 1, y, x + dx + 1, y);
752 
753                     dx += 2;
754                     x -= 1;
755                 }
756                 break;
757 
758             case EAST:
759                 g.setColor(lead);
760                 g.drawLine(s, cy, s, cy);
761                 for (int y = cy - 1, x = s - 1, dy = 1; x >= 1; x -= 2) {
762                     g.setColor(lead);
763                     g.drawLine(x, y, x, y);
764                     if (x <= 2) {
765                         g.drawLine(x - 1, y, x - 1, y + dy + 1);
766                     }
767                     g.setColor(fill);
768                     g.drawLine(x, y + 1, x, y + dy);
769                     if (x > 2) {
770                         g.drawLine(x - 1, y, x - 1, y + dy + 1);
771                     }
772                     g.setColor(trail);
773                     g.drawLine(x, y + dy + 1, x, y + dy + 1);
774 
775                     dy += 2;
776                     y -= 1;
777                 }
778                 break;
779 
780             case WEST:
781                 g.setColor(trail);
782                 g.drawLine(0, cy, 0, cy);
783                 for (int y = cy - 1, x = 1, dy = 1; x <= s - 2; x += 2) {
784                     g.setColor(lead);
785                     g.drawLine(x, y, x, y);
786                     if (x >= (s - 2)) {
787                         g.drawLine(x + 1, y, x + 1, y);
788                     }
789                     g.setColor(fill);
790                     g.drawLine(x, y + 1, x, y + dy);
791                     if (x < (s - 2)) {
792                         g.drawLine(x + 1, y, x + 1, y + dy + 1);
793                     }
794                     g.setColor(trail);
795                     g.drawLine(x, y + dy + 1, x, y + dy + 1);
796                     if (x >= (s - 2)) {
797                         g.drawLine(x + 1, y + 1, x + 1, y + dy + 1);
798                     }
799                     dy += 2;
800                     y -= 1;
801                 }
802                 break;
803             }
804         }
805     }
806 
807     final class XAWTScrollBarUI extends BasicScrollBarUI {
808 
809         @Override
installDefaults()810         protected void installDefaults()
811         {
812             super.installDefaults();
813             scrollbar.setBorder(new BevelBorder(false,SystemColor.controlDkShadow,SystemColor.controlLtHighlight) );
814         }
815 
816         @Override
configureScrollBarColors()817         protected void configureScrollBarColors() {
818             UIDefaults uidefaults = XToolkit.getUIDefaults();
819             Color bg = scrollbar.getBackground();
820             if (bg == null || bg instanceof UIResource) {
821                 scrollbar.setBackground(uidefaults.getColor("ScrollBar.background"));
822             }
823 
824             Color fg = scrollbar.getForeground();
825             if (fg == null || fg instanceof UIResource) {
826                 scrollbar.setForeground(uidefaults.getColor("ScrollBar.foreground"));
827             }
828 
829             thumbHighlightColor = uidefaults.getColor("ScrollBar.thumbHighlight");
830             thumbLightShadowColor = uidefaults.getColor("ScrollBar.thumbShadow");
831             thumbDarkShadowColor = uidefaults.getColor("ScrollBar.thumbDarkShadow");
832             thumbColor = uidefaults.getColor("ScrollBar.thumb");
833             trackColor = uidefaults.getColor("ScrollBar.track");
834 
835             trackHighlightColor = uidefaults.getColor("ScrollBar.trackHighlight");
836 
837         }
838 
839         @Override
createDecreaseButton(int orientation)840         protected JButton createDecreaseButton(int orientation) {
841             JButton b = new XAWTScrollBarButton(orientation);
842             return b;
843 
844         }
845 
846         @Override
createIncreaseButton(int orientation)847         protected JButton createIncreaseButton(int orientation) {
848             JButton b = new XAWTScrollBarButton(orientation);
849             return b;
850         }
851 
getDecreaseButton()852         public JButton getDecreaseButton(){
853             return decrButton;
854         }
855 
getIncreaseButton()856         public JButton getIncreaseButton(){
857             return incrButton;
858         }
859 
860         @Override
paint(Graphics g, JComponent c)861         public void paint(Graphics g, JComponent c) {
862             paintTrack(g, c, getTrackBounds());
863             Rectangle thumbBounds = getThumbBounds();
864             paintThumb(g, c, thumbBounds);
865         }
866 
867         @Override
paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)868         public void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
869         {
870             if(!scrollbar.isEnabled()) {
871                 return;
872             }
873 
874             if (thumbBounds.isEmpty())
875                 thumbBounds = getTrackBounds();
876 
877             int w = thumbBounds.width;
878             int h = thumbBounds.height;
879 
880             g.translate(thumbBounds.x, thumbBounds.y);
881             g.setColor(thumbColor);
882             g.fillRect(0, 0, w-1, h-1);
883 
884             g.setColor(thumbHighlightColor);
885             g.drawLine(0, 0, 0, h-1);
886             g.drawLine(1, 0, w-1, 0);
887 
888             g.setColor(thumbLightShadowColor);
889             g.drawLine(1, h-1, w-1, h-1);
890             g.drawLine(w-1, 1, w-1, h-2);
891 
892             g.translate(-thumbBounds.x, -thumbBounds.y);
893         }
894     }
895 
896     @SuppressWarnings("serial") // JDK-implementation class
897     final class AWTTextArea extends JTextArea implements DocumentListener {
898 
899         private boolean isFocused = false;
900         private final XTextAreaPeer peer;
901 
AWTTextArea(String text, XTextAreaPeer peer)902         AWTTextArea(String text, XTextAreaPeer peer) {
903             super(text);
904             setFocusable(false);
905             this.peer = peer;
906         }
907 
908         @Override
insertUpdate(DocumentEvent e)909         public void insertUpdate(DocumentEvent e) {
910             if (peer != null) {
911                 peer.postEvent(new TextEvent(peer.target,
912                                              TextEvent.TEXT_VALUE_CHANGED));
913             }
914         }
915 
916         @Override
removeUpdate(DocumentEvent e)917         public void removeUpdate(DocumentEvent e) {
918             if (peer != null) {
919                 peer.postEvent(new TextEvent(peer.target,
920                                              TextEvent.TEXT_VALUE_CHANGED));
921             }
922         }
923 
924         @Override
changedUpdate(DocumentEvent e)925         public void changedUpdate(DocumentEvent e) {
926             if (peer != null) {
927                 peer.postEvent(new TextEvent(peer.target,
928                                              TextEvent.TEXT_VALUE_CHANGED));
929             }
930         }
931 
forwardFocusGained( FocusEvent e)932         void forwardFocusGained( FocusEvent e) {
933             isFocused = true;
934             FocusEvent fe = new FocusEvent(this, e.getID(), e.isTemporary(),
935                     e.getOppositeComponent(), e.getCause());
936             super.processFocusEvent(fe);
937         }
938 
939 
forwardFocusLost( FocusEvent e)940         void forwardFocusLost( FocusEvent e) {
941             isFocused = false;
942             FocusEvent fe = new FocusEvent(this, e.getID(), e.isTemporary(),
943                     e.getOppositeComponent(), e.getCause());
944             super.processFocusEvent(fe);
945         }
946 
947         @Override
hasFocus()948         public boolean hasFocus() {
949             return isFocused;
950         }
951 
repaintNow()952         public void repaintNow() {
953             paintImmediately(getBounds());
954         }
955 
processMouseEventPublic(MouseEvent e)956         public void processMouseEventPublic(MouseEvent e) {
957             processMouseEvent(e);
958         }
959 
processMouseMotionEventPublic(MouseEvent e)960         public void processMouseMotionEventPublic(MouseEvent e) {
961             processMouseMotionEvent(e);
962         }
963 
processInputMethodEventPublic(InputMethodEvent e)964         public void processInputMethodEventPublic(InputMethodEvent e) {
965             processInputMethodEvent(e);
966         }
967 
968         @Override
updateUI()969         public void updateUI() {
970             ComponentUI ui = new AWTTextAreaUI();
971             setUI(ui);
972         }
973 
974         @Override
setTransferHandler(final TransferHandler newHandler)975         public void setTransferHandler(final TransferHandler newHandler) {
976             // override the default implementation to avoid loading
977             // SystemFlavorMap and associated classes
978             Object key = AWTAccessor.getClientPropertyKeyAccessor()
979                                     .getJComponent_TRANSFER_HANDLER();
980             Object oldHandler = getClientProperty(key);
981             putClientProperty(key, newHandler);
982             firePropertyChange("transferHandler", oldHandler, newHandler);
983         }
984     }
985 
986     final class XAWTScrollPaneUI extends BasicScrollPaneUI {
987 
988         private final Border vsbMarginBorderR = new EmptyBorder(0, 2, 0, 0);
989         private final Border vsbMarginBorderL = new EmptyBorder(0, 0, 0, 2);
990         private final Border hsbMarginBorder = new EmptyBorder(2, 0, 0, 0);
991 
992         private Border vsbBorder;
993         private Border hsbBorder;
994 
995         private PropertyChangeListener propertyChangeHandler;
996 
997         @Override
installListeners(JScrollPane scrollPane)998         protected void installListeners(JScrollPane scrollPane) {
999             super.installListeners(scrollPane);
1000             propertyChangeHandler = createPropertyChangeHandler();
1001             scrollPane.addPropertyChangeListener(propertyChangeHandler);
1002         }
1003 
1004         @Override
paint(Graphics g, JComponent c)1005         public void paint(Graphics g, JComponent c) {
1006             Border vpBorder = scrollpane.getViewportBorder();
1007             if (vpBorder != null) {
1008                 Rectangle r = scrollpane.getViewportBorderBounds();
1009                 vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
1010             }
1011         }
1012 
1013         @Override
uninstallListeners(JComponent scrollPane)1014         protected void uninstallListeners(JComponent scrollPane) {
1015             super.uninstallListeners(scrollPane);
1016             scrollPane.removePropertyChangeListener(propertyChangeHandler);
1017         }
1018 
createPropertyChangeHandler()1019         private PropertyChangeListener createPropertyChangeHandler() {
1020             return new PropertyChangeListener() {
1021                     @Override
1022                     public void propertyChange(PropertyChangeEvent e) {
1023                         String propertyName = e.getPropertyName();
1024 
1025                         if (propertyName.equals("componentOrientation")) {
1026                             JScrollPane pane = (JScrollPane)e.getSource();
1027                             JScrollBar vsb = pane.getVerticalScrollBar();
1028                             if (vsb != null) {
1029                                 if (isLeftToRight(pane)) {
1030                                     vsbBorder = new CompoundBorder(new EmptyBorder(0, 4, 0, -4),
1031                                                                    vsb.getBorder());
1032                                 } else {
1033                                     vsbBorder = new CompoundBorder(new EmptyBorder(0, -4, 0, 4),
1034                                                                    vsb.getBorder());
1035                                 }
1036                                 vsb.setBorder(vsbBorder);
1037                             }
1038                         }
1039                     }};
1040         }
1041 
isLeftToRight( Component c )1042         boolean isLeftToRight( Component c ) {
1043             return c.getComponentOrientation().isLeftToRight();
1044         }
1045 
1046         @Override
installDefaults(JScrollPane scrollpane)1047         protected void installDefaults(JScrollPane scrollpane) {
1048             Border b = scrollpane.getBorder();
1049             UIDefaults uidefaults = XToolkit.getUIDefaults();
1050             scrollpane.setBorder(uidefaults.getBorder("ScrollPane.border"));
1051             scrollpane.setBackground(uidefaults.getColor("ScrollPane.background"));
1052             scrollpane.setViewportBorder(uidefaults.getBorder("TextField.border"));
1053             JScrollBar vsb = scrollpane.getVerticalScrollBar();
1054             if (vsb != null) {
1055                 if (isLeftToRight(scrollpane)) {
1056                     vsbBorder = new CompoundBorder(vsbMarginBorderR,
1057                                                    vsb.getBorder());
1058                 }
1059                 else {
1060                     vsbBorder = new CompoundBorder(vsbMarginBorderL,
1061                                                    vsb.getBorder());
1062                 }
1063                 vsb.setBorder(vsbBorder);
1064             }
1065 
1066             JScrollBar hsb = scrollpane.getHorizontalScrollBar();
1067             if (hsb != null) {
1068                 hsbBorder = new CompoundBorder(hsbMarginBorder, hsb.getBorder());
1069                 hsb.setBorder(hsbBorder);
1070             }
1071         }
1072 
1073         @Override
uninstallDefaults(JScrollPane c)1074         protected void uninstallDefaults(JScrollPane c) {
1075             super.uninstallDefaults(c);
1076 
1077             JScrollBar vsb = scrollpane.getVerticalScrollBar();
1078             if (vsb != null) {
1079                 if (vsb.getBorder() == vsbBorder) {
1080                     vsb.setBorder(null);
1081                 }
1082                 vsbBorder = null;
1083             }
1084 
1085             JScrollBar hsb = scrollpane.getHorizontalScrollBar();
1086             if (hsb != null) {
1087                 if (hsb.getBorder() == hsbBorder) {
1088                     hsb.setBorder(null);
1089                 }
1090                 hsbBorder = null;
1091             }
1092         }
1093     }
1094 
1095     @SuppressWarnings("serial") // JDK-implementation class
1096     private class AWTTextPane extends JScrollPane implements FocusListener {
1097 
1098         private final JTextArea jtext;
1099         private final XWindow xwin;
1100 
1101         private final Color control = SystemColor.control;
1102         private final Color focus = SystemColor.activeCaptionBorder;
1103 
1104         AWTTextPane(JTextArea jt, XWindow xwin, Container parent) {
1105             super(jt);
1106             this.xwin = xwin;
1107             setDoubleBuffered(true);
1108             jt.addFocusListener(this);
1109             AWTAccessor.getComponentAccessor().setParent(this,parent);
1110             setViewportBorder(new BevelBorder(false,SystemColor.controlDkShadow,SystemColor.controlLtHighlight) );
1111             this.jtext = jt;
1112             setFocusable(false);
1113             addNotify();
1114         }
1115 
1116         @Override
1117         public void invalidate() {
1118             synchronized (getTreeLock()) {
1119                 final Container parent = getParent();
1120                 AWTAccessor.getComponentAccessor().setParent(this, null);
1121                 try {
1122                     super.invalidate();
1123                 } finally {
1124                     AWTAccessor.getComponentAccessor().setParent(this, parent);
1125                 }
1126             }
1127         }
1128 
1129         @Override
1130         public void focusGained(FocusEvent e) {
1131             Graphics g = getGraphics();
1132             Rectangle r = getViewportBorderBounds();
1133             g.setColor(focus);
1134             g.drawRect(r.x,r.y,r.width,r.height);
1135             g.dispose();
1136         }
1137 
1138         @Override
1139         public void focusLost(FocusEvent e) {
1140             Graphics g = getGraphics();
1141             Rectangle r = getViewportBorderBounds();
1142             g.setColor(control);
1143             g.drawRect(r.x,r.y,r.width,r.height);
1144             g.dispose();
1145         }
1146 
1147         public Window getRealParent() {
1148             return (Window) xwin.target;
1149         }
1150 
1151         @Override
1152         public void updateUI() {
1153             ComponentUI ui = new XAWTScrollPaneUI();
1154             setUI(ui);
1155         }
1156 
1157         @Override
1158         public JScrollBar createVerticalScrollBar() {
1159             return new XAWTScrollBar(JScrollBar.VERTICAL);
1160         }
1161 
1162         @Override
1163         public JScrollBar createHorizontalScrollBar() {
1164             return new XAWTScrollBar(JScrollBar.HORIZONTAL);
1165         }
1166 
1167         public JTextArea getTextArea () {
1168             return this.jtext;
1169         }
1170 
1171         @Override
1172         public Graphics getGraphics() {
1173             return xwin.getGraphics();
1174         }
1175 
1176         @SuppressWarnings("serial") // JDK-implementation class
1177         final class XAWTScrollBar extends ScrollBar {
1178 
1179             XAWTScrollBar(int i) {
1180                 super(i);
1181                 setFocusable(false);
1182             }
1183 
1184             @Override
1185             public void updateUI() {
1186                 ComponentUI ui = new XAWTScrollBarUI();
1187                 setUI(ui);
1188             }
1189         }
1190     }
1191 
1192     @SuppressWarnings("serial") // JDK-implementation class
1193     static class BevelBorder extends AbstractBorder implements UIResource {
1194         private Color darkShadow = SystemColor.controlDkShadow;
1195         private Color lightShadow = SystemColor.controlLtHighlight;
1196         private Color control = SystemColor.controlShadow;
1197         private boolean isRaised;
1198 
1199         BevelBorder(boolean isRaised, Color darkShadow, Color lightShadow) {
1200             this.isRaised = isRaised;
1201             this.darkShadow = darkShadow;
1202             this.lightShadow = lightShadow;
1203         }
1204 
1205         @Override
1206         public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
1207             g.setColor((isRaised) ? lightShadow : darkShadow);
1208             g.drawLine(x, y, x+w-1, y);           // top
1209             g.drawLine(x, y+h-1, x, y+1);         // left
1210 
1211             g.setColor(control);
1212             g.drawLine(x+1, y+1, x+w-2, y+1);           // top
1213             g.drawLine(x+1, y+h-1, x+1, y+1);         // left
1214 
1215             g.setColor((isRaised) ? darkShadow : lightShadow);
1216             g.drawLine(x+1, y+h-1, x+w-1, y+h-1); // bottom
1217             g.drawLine(x+w-1, y+h-1, x+w-1, y+1); // right
1218 
1219             g.setColor(control);
1220             g.drawLine(x+1, y+h-2, x+w-2, y+h-2); // bottom
1221             g.drawLine(x+w-2, y+h-2, x+w-2, y+1); // right
1222         }
1223 
1224         @Override
1225         public Insets getBorderInsets(Component c) {
1226             return getBorderInsets(c, new Insets(0,0,0,0));
1227         }
1228 
1229         @Override
1230         public Insets getBorderInsets(Component c, Insets insets) {
1231             insets.top = insets.left = insets.bottom = insets.right = 2;
1232             return insets;
1233         }
1234 
1235         public boolean isOpaque(Component c) {
1236             return true;
1237         }
1238     }
1239 
1240 
1241     // This class dispatches 'MouseEvent's to 'XTextAreaPeer''s (hidden)
1242     // subcomponents, and overrides mouse cursor, e.g. for scrollbars.
1243     //
1244     // However, current dispatching is a kind of fake, and is tuned to do only
1245     // what is necessary/possible. E.g. no additional mouse-exited/entered
1246     // events are generated, when mouse exits scrollbar and enters viewport
1247     // with JTextArea inside. Actually, no events are ever generated here (for
1248     // now). They are only dispatched as correctly as possible/neccessary.
1249     //
1250     // In future, it would be better to replace fake-emulation of grab-detection
1251     // and event-dispatching here, by reusing some common implementation of this
1252     // functionality. Mouse-cursor setting should also be freed of hacked
1253     // overloading here.
1254 
1255     private static final class JavaMouseEventHandler {
1256         private final XTextAreaPeer outer;
1257         private final Pointer current = new Pointer();
1258         private boolean grabbed = false;
1259 
1260         JavaMouseEventHandler( XTextAreaPeer outer ) {
1261             this.outer = outer;
1262         }
1263 
1264 
1265         // 1. We can make grab-tracking emulation here more robust to variations in
1266         //    in mouse-events order and consistence. E.g. by using such code:
1267         //    if( grabbed && event.getID()==MouseEvent.MOUSE_MOVED ) grabbed = false;
1268         //    Or we can also use 'assert'ions.
1269         // 2. WARNING: Currently, while grab-detection mechanism _here_ says, that
1270         //    grab is in progress, we do not update 'current'.  In case 'current'
1271         //    is set to a scrollbar or to a scroll-button, then references to their
1272         //    'Component'-instances are "remembered". And events are dispatched to
1273         //    these remembered components, without checking, if XTextAreaPeer has
1274         //    replaced these instances with another ones. This also aplies to
1275         //    mouse-drags-from-outside (see comment in 'grabbed_update' method).
1276 
1277         void handle( MouseEvent event ) {
1278             if ( ! grabbed ) {
1279                 // dispatch() needs up-to-date pointer in ungrabbed case.
1280                 setPointerToUnderPoint( event.getPoint() );
1281             }
1282             dispatch( event );
1283             boolean wasGrabbed = grabbed;
1284             grabbed_update( event );
1285             if ( wasGrabbed && ! grabbed ) {
1286                 setPointerToUnderPoint( event.getPoint() );
1287             }
1288             setCursor();
1289         }
1290 
1291         // Following is internally private:
1292 
1293         // Here dispatching is performed, of 'MouseEvent's to (some)
1294         // 'XTextAreaPeer''s (hidden) subcomponents.
1295         private void dispatch( MouseEvent event ) {
1296             switch( current.getType() )
1297             {
1298                 case TEXT:
1299                     Point point = toViewportChildLocalSpace(
1300                         outer.textPane.getViewport(), event.getPoint() );
1301                     XTextAreaPeer.AWTTextArea jtext = outer.jtext;
1302                     MouseEvent newEvent = newMouseEvent( jtext, point, event );
1303                     int id = newEvent.getID();
1304                     if ( id==MouseEvent.MOUSE_MOVED || id==MouseEvent.MOUSE_DRAGGED ) {
1305                         jtext.processMouseMotionEventPublic( newEvent );
1306                     } else {
1307                         jtext.processMouseEventPublic( newEvent );
1308                     }
1309                     break;
1310 
1311                 // We perform (additional) dispatching of events to buttons of
1312                 // scrollbar, instead of leaving it to JScrollbar. This is
1313                 // required, because of different listeners in Swing and AWT,
1314                 // which trigger scrolling (ArrowButtonListener vs. TrackListener,
1315                 // accordingly). So we dispatch events to scroll-buttons, to
1316                 // invoke a correct Swing button listener.
1317                 // See CR 6175401 for more information.
1318                 case BAR:
1319                 case BUTTON:
1320                     Component c = current.getBar();
1321                     Point p = toLocalSpace( c, event.getPoint() );
1322                     if ( current.getType()==Pointer.Type.BUTTON ) {
1323                         c = current.getButton();
1324                         p = toLocalSpace( c, p );
1325                     }
1326                     AWTAccessor.getComponentAccessor().processEvent( c, newMouseEvent( c, p, event ) );
1327                     break;
1328             }
1329         }
1330 
1331         @SuppressWarnings("deprecation")
1332         private static MouseEvent newMouseEvent(
1333             Component source, Point point, MouseEvent template )
1334         {
1335             MouseEvent e = template;
1336             MouseEvent nme = new MouseEvent(
1337                 source,
1338                 e.getID(), e.getWhen(),
1339                 e.getModifiersEx() | e.getModifiers(),
1340                 point.x, point.y,
1341                 e.getXOnScreen(), e.getYOnScreen(),
1342                 e.getClickCount(), e.isPopupTrigger(), e.getButton() );
1343             // Because these MouseEvents are dispatched directly to
1344             // their target, we need to mark them as being
1345             // system-generated here
1346             SunToolkit.setSystemGenerated(nme);
1347             return nme;
1348         }
1349 
1350         private void setCursor() {
1351             if ( current.getType()==Pointer.Type.TEXT ) {
1352                 // 'target.getCursor()' is also applied from elsewhere
1353                 // (at least now), but only when mouse "entered", and
1354                 // before 'XTextAreaPeer.handleJavaMouseEvent' is invoked.
1355                 outer.pSetCursor( outer.target.getCursor(), true );
1356             }
1357             else {
1358                 // We can write here a more intelligent cursor selection
1359                 // mechanism, like getting cursor from 'current' component.
1360                 // However, I see no point in doing so now. But if you feel
1361                 // like implementing it, you'll probably need to introduce
1362                 // 'Pointer.Type.PANEL'.
1363                 outer.pSetCursor( outer.textPane.getCursor(), true );
1364             }
1365         }
1366 
1367 
1368         // Current way of grab-detection causes interesting (but harmless)
1369         // side-effect. If mouse is draged from outside to inside of TextArea,
1370         // we will then (in some cases) be asked to dispatch mouse-entered/exited
1371         // events. But, as at least one mouse-button is down, we will detect
1372         // grab-mode is on (though the grab isn't ours).
1373         //
1374         // Thus, we will not update 'current' (see 'handle' method), and will
1375         // dispatch events to the last subcomponent, the 'current' was set to.
1376         // As always, we set cursor in this case also. But, all this seems
1377         // harmless, because mouse entered/exited events seem to have no effect
1378         // here, and cursor setting is ignored in case of drags from outside.
1379         //
1380         // Grab-detection can be further improved, e.g. by taking into account
1381         // current event-ID, but I see not point in doing it now.
1382 
1383         private void grabbed_update( MouseEvent event ) {
1384             final int allButtonsMask
1385                 = MouseEvent.BUTTON1_DOWN_MASK
1386                 | MouseEvent.BUTTON2_DOWN_MASK
1387                 | MouseEvent.BUTTON3_DOWN_MASK;
1388             grabbed = ( (event.getModifiersEx() & allButtonsMask) != 0 );
1389         }
1390 
1391         // 'toLocalSpace' and 'toViewportChildLocalSpace' can be "optimized" to
1392         // 'return' 'void' and use 'Point' input-argument also as output.
1393         private static Point toLocalSpace( Component local, Point inParentSpace )
1394         {
1395             Point p = inParentSpace;
1396             Point l = local.getLocation();
1397             return new Point( p.x - l.x, p.y - l.y );
1398         }
1399         private static Point toViewportChildLocalSpace( JViewport v, Point inViewportParentSpace )
1400         {
1401             Point l = toLocalSpace(v, inViewportParentSpace);
1402             Point p = v.getViewPosition();
1403             l.x += p.x;
1404             l.y += p.y;
1405             return l;
1406         }
1407 
1408         private void setPointerToUnderPoint( Point point ) {
1409             if ( outer.textPane.getViewport().getBounds().contains( point ) ) {
1410                 current.setText();
1411             }
1412             else if ( ! setPointerIfPointOverScrollbar(
1413                 outer.textPane.getVerticalScrollBar(), point ) )
1414             {
1415                 if ( ! setPointerIfPointOverScrollbar(
1416                     outer.textPane.getHorizontalScrollBar(), point ) )
1417                 {
1418                     current.setNone();
1419                 }
1420             }
1421         }
1422 
1423         private boolean setPointerIfPointOverScrollbar( JScrollBar bar, Point point ) {
1424             if ( ! bar.getBounds().contains( point ) ) {
1425                 return false;
1426             }
1427             current.setBar( bar );
1428             Point local = toLocalSpace( bar, point );
1429 
1430             XTextAreaPeer.XAWTScrollBarUI ui =
1431                 (XTextAreaPeer.XAWTScrollBarUI) bar.getUI();
1432 
1433             if ( ! setPointerIfPointOverButton( ui.getIncreaseButton(), local ) ) {
1434                 setPointerIfPointOverButton( ui.getDecreaseButton(), local );
1435             }
1436 
1437             return true;
1438         }
1439 
1440         private boolean setPointerIfPointOverButton( JButton button, Point point ) {
1441             if ( ! button.getBounds().contains( point ) ) {
1442                 return false;
1443             }
1444             current.setButton( button );
1445             return true;
1446         }
1447 
1448         private static final class Pointer {
1449             static enum Type {
1450                 NONE, TEXT, BAR, BUTTON  // , PANEL
1451             }
1452             Type getType() {
1453                 return type;
1454             }
1455             boolean isNone() {
1456                 return type==Type.NONE;
1457             }
1458             JScrollBar getBar() {
1459                 boolean ok = type==Type.BAR || type==Type.BUTTON;
1460                 assert ok;
1461                 return ok ? bar : null;
1462             }
1463             JButton getButton() {
1464                 boolean ok = type==Type.BUTTON;
1465                 assert ok;
1466                 return ok ? button : null;
1467             }
1468             void setNone() {
1469                 type = Type.NONE;
1470             }
1471             void setText() {
1472                 type = Type.TEXT;
1473             }
1474             void setBar( JScrollBar bar ) {
1475                 this.bar=bar;
1476                 type=Type.BAR;
1477             }
1478             void setButton( JButton button ) {
1479                 this.button=button;
1480                 type=Type.BUTTON;
1481             }
1482 
1483             private Type type;
1484             private JScrollBar bar;
1485             private JButton button;
1486         }
1487     }
1488 }
1489