1 /*
   2  * Copyright (c) 2003, 2013, 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.event.MouseEvent;
  30 import java.awt.event.MouseWheelEvent;
  31 import java.awt.event.AdjustmentEvent;
  32 import java.util.ArrayList;
  33 import java.util.Iterator;
  34 import sun.util.logging.PlatformLogger;
  35 
  36 // FIXME: implement multi-select
  37 /*
  38  * Class to paint a list of items, possibly with scrollbars
  39  * This class paints all items with the same font
  40  * For now, this class manages the list of items and painting thereof, but not
  41  * posting of Item or ActionEvents
  42  */
  43 public class ListHelper implements XScrollbarClient {
  44     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.ListHelper");
  45 
  46     private final int FOCUS_INSET = 1;
  47 
  48     private final int BORDER_WIDTH; // Width of border drawn around the list
  49                                     // of items
  50     private final int ITEM_MARGIN;  // Margin between the border of the list
  51                                     // of items and and item's bg, and between
  52                                     // items
  53     private final int TEXT_SPACE;   // Space between the edge of an item and
  54                                     // the text
  55 
  56     private final int SCROLLBAR_WIDTH;  // Width of a scrollbar
  57 
  58     private java.util.List items;        // List of items
  59 
  60     // TODO: maybe this would be better as a simple int[]
  61     private java.util.List selected;     // List of selected items
  62     private boolean multiSelect;         // Can multiple items be selected
  63                                          // at once?
  64     private int focusedIndex;
  65 
  66     private int maxVisItems;             // # items visible without a vsb
  67     private XVerticalScrollbar vsb;      // null if unsupported
  68     private boolean vsbVis;
  69     private XHorizontalScrollbar hsb;    // null if unsupported
  70     private boolean hsbVis;
  71 
  72     private Font font;
  73     private FontMetrics fm;
  74 
  75     private XWindow peer;   // So far, only needed for painting
  76                             // on notifyValue()
  77     private Color[] colors; // Passed in for painting on notifyValue()
  78 
  79     // Holds the true if mouse is dragging outside of the area of the list
  80     // The flag is used at the moment of the dragging and releasing mouse
  81     // See 6243382 for more information
  82     boolean mouseDraggedOutVertically = false;
  83     private volatile boolean vsbVisibilityChanged = false;
  84 
  85     /*
  86      * Comment
  87      */
  88     public ListHelper(XWindow peer,
  89                       Color[] colors,
  90                       int initialSize,
  91                       boolean multiSelect,
  92                       boolean scrollVert,
  93                       boolean scrollHoriz,
  94                       Font font,
  95                       int maxVisItems,
  96                       int SPACE,
  97                       int MARGIN,
  98                       int BORDER,
  99                       int SCROLLBAR) {
 100         this.peer = peer;
 101         this.colors = colors;
 102         this.multiSelect = multiSelect;
 103         items = new ArrayList(initialSize);
 104         selected = new ArrayList(1);
 105         selected.add(Integer.valueOf(-1));
 106 
 107         this.maxVisItems = maxVisItems;
 108         if (scrollVert) {
 109             vsb = new XVerticalScrollbar(this);
 110             vsb.setValues(0, 0, 0, 0, 1, maxVisItems - 1);
 111         }
 112         if (scrollHoriz) {
 113             hsb = new XHorizontalScrollbar(this);
 114             hsb.setValues(0, 0, 0, 0, 1, 1);
 115         }
 116 
 117         setFont(font);
 118         TEXT_SPACE = SPACE;
 119         ITEM_MARGIN = MARGIN;
 120         BORDER_WIDTH = BORDER;
 121         SCROLLBAR_WIDTH = SCROLLBAR;
 122     }
 123 
 124     public Component getEventSource() {
 125         return peer.getEventSource();
 126     }
 127 
 128     /**********************************************************************/
 129     /* List management methods                                            */
 130     /**********************************************************************/
 131 
 132     public void add(String item) {
 133         items.add(item);
 134         updateScrollbars();
 135     }
 136 
 137     public void add(String item, int index) {
 138         items.add(index, item);
 139         updateScrollbars();
 140     }
 141 
 142     public void remove(String item) {
 143         // FIXME: need to clean up select list, too?
 144         items.remove(item);
 145         updateScrollbars();
 146         // Is vsb visible now?
 147     }
 148 
 149     public void remove(int index) {
 150         // FIXME: need to clean up select list, too?
 151         items.remove(index);
 152         updateScrollbars();
 153         // Is vsb visible now?
 154     }
 155 
 156     public void removeAll() {
 157         items.removeAll(items);
 158         updateScrollbars();
 159     }
 160 
 161     public void setMultiSelect(boolean ms) {
 162         multiSelect = ms;
 163     }
 164 
 165     /*
 166      * docs.....definitely docs
 167      * merely keeps internal track of which items are selected for painting
 168      * dealing with target Components happens elsewhere
 169      */
 170     public void select(int index) {
 171         if (index > getItemCount() - 1) {
 172             index = (isEmpty() ? -1 : 0);
 173         }
 174         if (multiSelect) {
 175             assert false : "Implement ListHelper.select() for multiselect";
 176         }
 177         else if (getSelectedIndex() != index) {
 178             selected.remove(0);
 179             selected.add(Integer.valueOf(index));
 180             makeVisible(index);
 181         }
 182     }
 183 
 184     /* docs */
 185     public void deselect(int index) {
 186         assert(false);
 187     }
 188 
 189     /* docs */
 190     /* if called for multiselect, return -1 */
 191     public int getSelectedIndex() {
 192         if (!multiSelect) {
 193             Integer val = (Integer)selected.get(0);
 194             return val.intValue();
 195         }
 196         return -1;
 197     }
 198 
 199     int[] getSelectedIndexes() { assert(false); return null;}
 200 
 201     /*
 202      * A getter method for XChoicePeer.
 203      * Returns vsbVisiblityChanged value and sets it to false.
 204      */
 205     public boolean checkVsbVisibilityChangedAndReset(){
 206         boolean returnVal = vsbVisibilityChanged;
 207         vsbVisibilityChanged = false;
 208         return returnVal;
 209     }
 210 
 211     public boolean isEmpty() {
 212         return items.isEmpty();
 213     }
 214 
 215     public int getItemCount() {
 216         return items.size();
 217     }
 218 
 219     public String getItem(int index) {
 220         return (String) items.get(index);
 221     }
 222 
 223     /**********************************************************************/
 224     /* GUI-related methods                                                */
 225     /**********************************************************************/
 226 
 227     public void setFocusedIndex(int index) {
 228         focusedIndex = index;
 229     }
 230 
 231     public boolean isFocusedIndex(int index) {
 232         return index == focusedIndex;
 233     }
 234 
 235     public void setFont(Font newFont) {
 236         if (newFont != font) {
 237             font = newFont;
 238             fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
 239             // Also cache stuff like fontHeight?
 240         }
 241     }
 242 
 243     /*
 244      * Returns width of the text of the longest item
 245      */
 246     public int getMaxItemWidth() {
 247         int m = 0;
 248         int end = getItemCount();
 249         for(int i = 0 ; i < end ; i++) {
 250             int l = fm.stringWidth(getItem(i));
 251             m = Math.max(m, l);
 252         }
 253         return m;
 254     }
 255 
 256     /*
 257      * Height of an item (this doesn't include ITEM_MARGIN)
 258      */
 259     int getItemHeight() {
 260         return fm.getHeight() + (2*TEXT_SPACE);
 261     }
 262 
 263     public int y2index(int y) {
 264         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 265             log.fine("y=" + y +", firstIdx=" + firstDisplayedIndex() +", itemHeight=" + getItemHeight()
 266                      + ",item_margin=" + ITEM_MARGIN);
 267         }
 268         // See 6243382 for more information
 269         int newIdx = firstDisplayedIndex() + ((y - 2*ITEM_MARGIN) / (getItemHeight() + 2*ITEM_MARGIN));
 270         return newIdx;
 271     }
 272 
 273     /* write these
 274     int index2y(int);
 275     public int numItemsDisplayed() {}
 276     */
 277 
 278     public int firstDisplayedIndex() {
 279         if (vsbVis) {
 280             return vsb.getValue();
 281         }
 282         return 0;
 283     }
 284 
 285     public int lastDisplayedIndex() {
 286         // FIXME: need to account for horiz scroll bar
 287         if (hsbVis) {
 288             assert false : "Implement for horiz scroll bar";
 289         }
 290 
 291         return vsbVis ? vsb.getValue() + maxVisItems - 1: getItemCount() - 1;
 292     }
 293 
 294     /*
 295      * If the given index is not visible in the List, scroll so that it is.
 296      */
 297     public void makeVisible(int index) {
 298         if (vsbVis) {
 299             if (index < firstDisplayedIndex()) {
 300                 vsb.setValue(index);
 301             }
 302             else if (index > lastDisplayedIndex()) {
 303                 vsb.setValue(index - maxVisItems + 1);
 304             }
 305         }
 306     }
 307 
 308     // FIXME: multi-select needs separate focused index
 309     public void up() {
 310         int curIdx = getSelectedIndex();
 311         int numItems = getItemCount();
 312         int newIdx;
 313 
 314         assert curIdx >= 0;
 315 
 316         if (curIdx == 0) {
 317             newIdx = numItems - 1;
 318         }
 319         else {
 320             newIdx = --curIdx;
 321         }
 322         // focus(newIdx);
 323         select(newIdx);
 324     }
 325 
 326     public void down() {
 327         int newIdx = (getSelectedIndex() + 1) % getItemCount();
 328         select(newIdx);
 329     }
 330 
 331     public void pageUp() {
 332         // FIXME: for multi-select, move the focused item, not the selected item
 333         if (vsbVis && firstDisplayedIndex() > 0) {
 334             if (multiSelect) {
 335                 assert false : "Implement pageUp() for multiSelect";
 336             }
 337             else {
 338                 int selectionOffset = getSelectedIndex() - firstDisplayedIndex();
 339                 // the vsb does bounds checking
 340                 int newIdx = firstDisplayedIndex() - vsb.getBlockIncrement();
 341                 vsb.setValue(newIdx);
 342                 select(firstDisplayedIndex() + selectionOffset);
 343             }
 344         }
 345     }
 346     public void pageDown() {
 347         if (vsbVis && lastDisplayedIndex() < getItemCount() - 1) {
 348             if (multiSelect) {
 349                 assert false : "Implement pageDown() for multiSelect";
 350             }
 351             else {
 352                 int selectionOffset = getSelectedIndex() - firstDisplayedIndex();
 353                 // the vsb does bounds checking
 354                 int newIdx = lastDisplayedIndex();
 355                 vsb.setValue(newIdx);
 356                 select(firstDisplayedIndex() + selectionOffset);
 357             }
 358         }
 359     }
 360     public void home() {}
 361     public void end() {}
 362 
 363 
 364     public boolean isVSBVisible() { return vsbVis; }
 365     public boolean isHSBVisible() { return hsbVis; }
 366 
 367     public XVerticalScrollbar getVSB() { return vsb; }
 368     public XHorizontalScrollbar getHSB() { return hsb; }
 369 
 370     public boolean isInVertSB(Rectangle bounds, int x, int y) {
 371         if (vsbVis) {
 372             assert vsb != null : "Vert scrollbar is visible, yet is null?";
 373             int sbHeight = hsbVis ? bounds.height - SCROLLBAR_WIDTH : bounds.height;
 374             return (x <= bounds.width) &&
 375                    (x >= bounds.width - SCROLLBAR_WIDTH) &&
 376                    (y >= 0) &&
 377                    (y <= sbHeight);
 378         }
 379         return false;
 380     }
 381 
 382     public boolean isInHorizSB(Rectangle bounds, int x, int y) {
 383         if (hsbVis) {
 384             assert hsb != null : "Horiz scrollbar is visible, yet is null?";
 385 
 386             int sbWidth = vsbVis ? bounds.width - SCROLLBAR_WIDTH : bounds.width;
 387             return (x <= sbWidth) &&
 388                    (x >= 0) &&
 389                    (y >= bounds.height - SCROLLBAR_WIDTH) &&
 390                    (y <= bounds.height);
 391         }
 392         return false;
 393     }
 394 
 395     public void handleVSBEvent(MouseEvent e, Rectangle bounds, int x, int y) {
 396         int sbHeight = hsbVis ? bounds.height - SCROLLBAR_WIDTH : bounds.height;
 397 
 398         vsb.handleMouseEvent(e.getID(),
 399                              e.getModifiers(),
 400                              x - (bounds.width - SCROLLBAR_WIDTH),
 401                              y);
 402     }
 403 
 404     /*
 405      * Called when items are added/removed.
 406      * Update whether the scrollbar is visible or not, scrollbar values
 407      */
 408     void updateScrollbars() {
 409         boolean oldVsbVis = vsbVis;
 410         vsbVis = vsb != null && items.size() > maxVisItems;
 411         if (vsbVis) {
 412             vsb.setValues(vsb.getValue(), getNumItemsDisplayed(),
 413                           vsb.getMinimum(), items.size());
 414         }
 415 
 416         // 6405689. If Vert Scrollbar gets disappeared from the dropdown menu we should repaint whole dropdown even if
 417         // no actual resize gets invoked. This is needed because some painting artifacts remained between dropdown items
 418         // but draw3DRect doesn't clear the area inside. Instead it just paints lines as borders.
 419         vsbVisibilityChanged = (vsbVis != oldVsbVis);
 420         // FIXME: check if added item makes a hsb necessary (if supported, that of course)
 421     }
 422 
 423     public int getNumItemsDisplayed() {
 424         return items.size() > maxVisItems ? maxVisItems : items.size();
 425     }
 426 
 427     public void repaintScrollbarRequest(XScrollbar sb) {
 428         Graphics g = peer.getGraphics();
 429         Rectangle bounds = peer.getBounds();
 430         if ((sb == vsb) && vsbVis) {
 431             paintVSB(g, XComponentPeer.getSystemColors(), bounds);
 432         }
 433         else if ((sb == hsb) && hsbVis) {
 434             paintHSB(g, XComponentPeer.getSystemColors(), bounds);
 435         }
 436         g.dispose();
 437     }
 438 
 439     public void notifyValue(XScrollbar obj, int type, int v, boolean isAdjusting) {
 440         if (obj == vsb) {
 441             int oldScrollValue = vsb.getValue();
 442             vsb.setValue(v);
 443             boolean needRepaint = (oldScrollValue != vsb.getValue());
 444             // See 6243382 for more information
 445             if (mouseDraggedOutVertically){
 446                 int oldItemValue = getSelectedIndex();
 447                 int newItemValue = getSelectedIndex() + v - oldScrollValue;
 448                 select(newItemValue);
 449                 needRepaint = needRepaint || (getSelectedIndex() != oldItemValue);
 450             }
 451 
 452             // FIXME: how are we going to paint!?
 453             Graphics g = peer.getGraphics();
 454             Rectangle bounds = peer.getBounds();
 455             int first = v;
 456             int last = Math.min(getItemCount() - 1,
 457                                 v + maxVisItems);
 458             if (needRepaint) {
 459                 paintItems(g, colors, bounds, first, last);
 460             }
 461             g.dispose();
 462 
 463         }
 464         else if ((XHorizontalScrollbar)obj == hsb) {
 465             hsb.setValue(v);
 466             // FIXME: how are we going to paint!?
 467         }
 468     }
 469 
 470     public void updateColors(Color[] newColors) {
 471         colors = newColors;
 472     }
 473 
 474     /*
 475     public void paintItems(Graphics g,
 476                            Color[] colors,
 477                            Rectangle bounds,
 478                            Font font,
 479                            int first,
 480                            int last,
 481                            XVerticalScrollbar vsb,
 482                            XHorizontalScrollbar hsb) {
 483     */
 484     public void paintItems(Graphics g,
 485                            Color[] colors,
 486                            Rectangle bounds) {
 487         // paint border
 488         // paint items
 489         // paint scrollbars
 490         // paint focus?
 491 
 492     }
 493     public void paintAllItems(Graphics g,
 494                            Color[] colors,
 495                            Rectangle bounds) {
 496         paintItems(g, colors, bounds,
 497                    firstDisplayedIndex(), lastDisplayedIndex());
 498     }
 499     public void paintItems(Graphics g,
 500                            Color[] colors,
 501                            Rectangle bounds,
 502                            int first,
 503                            int last) {
 504         peer.flush();
 505         int x = BORDER_WIDTH + ITEM_MARGIN;
 506         int width = bounds.width - 2*ITEM_MARGIN - 2*BORDER_WIDTH - (vsbVis ? SCROLLBAR_WIDTH : 0);
 507         int height = getItemHeight();
 508         int y = BORDER_WIDTH + ITEM_MARGIN;
 509 
 510         for (int i = first; i <= last ; i++) {
 511             paintItem(g, colors, getItem(i),
 512                       x, y, width, height,
 513                       isItemSelected(i),
 514                       isFocusedIndex(i));
 515             y += height + 2*ITEM_MARGIN;
 516         }
 517 
 518         if (vsbVis) {
 519             paintVSB(g, XComponentPeer.getSystemColors(), bounds);
 520         }
 521         if (hsbVis) {
 522             paintHSB(g, XComponentPeer.getSystemColors(), bounds);
 523         }
 524         peer.flush();
 525         // FIXME: if none of the items were focused, paint focus around the
 526         // entire list.  This is how java.awt.List should work.
 527     }
 528 
 529     /*
 530      * comment about what is painted (i.e. the focus rect
 531      */
 532     public void paintItem(Graphics g,
 533                           Color[] colors,
 534                           String string,
 535                           int x, int y, int width, int height,
 536                           boolean selected,
 537                           boolean focused) {
 538         //System.out.println("LP.pI(): x="+x+" y="+y+" w="+width+" h="+height);
 539         //g.setColor(colors[BACKGROUND_COLOR]);
 540 
 541         // FIXME: items shouldn't draw into the scrollbar
 542 
 543         if (selected) {
 544             g.setColor(colors[XComponentPeer.FOREGROUND_COLOR]);
 545         }
 546         else {
 547             g.setColor(colors[XComponentPeer.BACKGROUND_COLOR]);
 548         }
 549         g.fillRect(x, y, width, height);
 550 
 551         if (focused) {
 552             //g.setColor(colors[XComponentPeer.FOREGROUND_COLOR]);
 553             g.setColor(Color.BLACK);
 554             g.drawRect(x + FOCUS_INSET,
 555                        y + FOCUS_INSET,
 556                        width - 2*FOCUS_INSET,
 557                        height - 2*FOCUS_INSET);
 558         }
 559 
 560         if (selected) {
 561             g.setColor(colors[XComponentPeer.BACKGROUND_COLOR]);
 562         }
 563         else {
 564             g.setColor(colors[XComponentPeer.FOREGROUND_COLOR]);
 565         }
 566         g.setFont(font);
 567         //Rectangle clip = g.getClipBounds();
 568         //g.clipRect(x, y, width, height);
 569         //g.drawString(string, x + TEXT_SPACE, y + TEXT_SPACE + ITEM_MARGIN);
 570 
 571         int fontAscent = fm.getAscent();
 572         int fontDescent = fm.getDescent();
 573 
 574         g.drawString(string, x + TEXT_SPACE, y + (height + fm.getMaxAscent() - fm.getMaxDescent())/2);
 575         //g.clipRect(clip.x, clip.y, clip.width, clip.height);
 576     }
 577 
 578     boolean isItemSelected(int index) {
 579         Iterator itr = selected.iterator();
 580         while (itr.hasNext()) {
 581             Integer val = (Integer)itr.next();
 582             if (val.intValue() == index) {
 583                 return true;
 584             }
 585         }
 586         return false;
 587     }
 588 
 589     public void paintVSB(Graphics g, Color colors[], Rectangle bounds) {
 590         int height = bounds.height - 2*BORDER_WIDTH - (hsbVis ? (SCROLLBAR_WIDTH-2) : 0);
 591         Graphics ng = g.create();
 592 
 593         g.setColor(colors[XComponentPeer.BACKGROUND_COLOR]);
 594         try {
 595             ng.translate(bounds.width - BORDER_WIDTH - SCROLLBAR_WIDTH,
 596                          BORDER_WIDTH);
 597             // Update scrollbar's size
 598             vsb.setSize(SCROLLBAR_WIDTH, bounds.height);
 599             vsb.paint(ng, colors, true);
 600         } finally {
 601             ng.dispose();
 602         }
 603     }
 604 
 605     public void paintHSB(Graphics g, Color colors[], Rectangle bounds) {
 606 
 607     }
 608 
 609     /*
 610      * Helper method for Components with integrated scrollbars.
 611      * Pass in the vertical and horizontal scroll bar (or null for none/hidden)
 612      * and the MouseWheelEvent, and the appropriate scrollbar will be scrolled
 613      * correctly.
 614      * Returns whether or not scrolling actually took place.  This will indicate
 615      * whether or not repainting is required.
 616      */
 617     static boolean doWheelScroll(XVerticalScrollbar vsb,
 618                                      XHorizontalScrollbar hsb,
 619                                      MouseWheelEvent e) {
 620         XScrollbar scroll = null;
 621         int wheelRotation;
 622 
 623         // Determine which, if any, sb to scroll
 624         if (vsb != null) {
 625             scroll = vsb;
 626         }
 627         else if (hsb != null) {
 628             scroll = hsb;
 629         }
 630         else { // Neither scrollbar is showing
 631             return false;
 632         }
 633 
 634         wheelRotation = e.getWheelRotation();
 635 
 636         // Check if scroll is necessary
 637         if ((wheelRotation < 0 && scroll.getValue() > scroll.getMinimum()) ||
 638             (wheelRotation > 0 && scroll.getValue() < scroll.getMaximum()) ||
 639             wheelRotation != 0) {
 640 
 641             int type = e.getScrollType();
 642             int incr;
 643             if (type == MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
 644                 incr = wheelRotation * scroll.getBlockIncrement();
 645             }
 646             else { // type is WHEEL_UNIT_SCROLL
 647                 incr = e.getUnitsToScroll() * scroll.getUnitIncrement();
 648             }
 649             scroll.setValue(scroll.getValue() + incr);
 650             return true;
 651         }
 652         return false;
 653     }
 654 
 655     /*
 656      * Helper method for XChoicePeer with integrated vertical scrollbar.
 657      * Start or stop vertical scrolling when mouse dragged in / out the area of the list if it's required
 658      * Restoring Motif behavior
 659      * See 6243382 for more information
 660      */
 661     void trackMouseDraggedScroll(int mouseX, int mouseY, int listWidth, int listHeight){
 662 
 663         if (!mouseDraggedOutVertically){
 664             if (vsb.beforeThumb(mouseX, mouseY)) {
 665                 vsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
 666             } else {
 667                 vsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
 668             }
 669         }
 670 
 671         if(!mouseDraggedOutVertically && (mouseY < 0 || mouseY >= listHeight)){
 672             mouseDraggedOutVertically = true;
 673             vsb.startScrollingInstance();
 674         }
 675 
 676         if (mouseDraggedOutVertically && mouseY >= 0 && mouseY < listHeight && mouseX >= 0 && mouseX < listWidth){
 677             mouseDraggedOutVertically = false;
 678             vsb.stopScrollingInstance();
 679         }
 680     }
 681 
 682     /*
 683      * Helper method for XChoicePeer with integrated vertical scrollbar.
 684      * Stop vertical scrolling when mouse released in / out the area of the list if it's required
 685      * Restoring Motif behavior
 686      * see 6243382 for more information
 687      */
 688     void trackMouseReleasedScroll(){
 689 
 690         if (mouseDraggedOutVertically){
 691             mouseDraggedOutVertically = false;
 692             vsb.stopScrollingInstance();
 693         }
 694 
 695     }
 696 }