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