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