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