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