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
27// Very much based on XListPeer from javaos
28
29package sun.awt.X11;
30
31import java.awt.*;
32import java.awt.event.*;
33import java.awt.peer.*;
34import java.util.Objects;
35import java.util.Vector;
36import java.awt.image.*;
37import sun.util.logging.PlatformLogger;
38
39// TODO: some input actions should do nothing if Shift or Control are down
40
41class XListPeer extends XComponentPeer implements ListPeer, XScrollbarClient {
42
43    private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XListPeer");
44
45    public static final int     MARGIN = 2;
46    public static final int     SPACE = 1;
47    public static final int     SCROLLBAR_AREA = 17;  // Area reserved for the
48                                                      // scrollbar
49    public static final int     SCROLLBAR_WIDTH = 13; // Actual width of the
50                                                      // scrollbar
51    public static final int     NONE = -1;
52    public static final int     WINDOW = 0;
53    public static final int     VERSCROLLBAR = 1;
54    public static final int     HORSCROLLBAR = 2;
55    public static final int     DEFAULT_VISIBLE_ROWS = 4; // From java.awt.List,
56    public static final int     HORIZ_SCROLL_AMT = 10;
57
58    private static final int    PAINT_VSCROLL = 2;
59    private static final int    PAINT_HSCROLL = 4;
60    private static final int    PAINT_ITEMS = 8;
61    private static final int    PAINT_FOCUS = 16;
62    private static final int    PAINT_BACKGROUND = 32;
63    private static final int    PAINT_HIDEFOCUS = 64;
64    private static final int    PAINT_ALL =
65        PAINT_VSCROLL | PAINT_HSCROLL | PAINT_ITEMS | PAINT_FOCUS | PAINT_BACKGROUND;
66    private static final int    COPY_AREA = 128;
67
68    XVerticalScrollbar       vsb;
69    XHorizontalScrollbar     hsb;
70    ListPainter painter;
71
72    // TODO: ick - Vector?
73    Vector<String>              items;
74    boolean                     multipleSelections;
75    int                         active = NONE;
76
77    // Holds the array of the indexes of the elements which is selected
78    // This array should be kept sorted, low to high.
79    int                         selected[];
80    int                         fontHeight;
81    int                         fontAscent;
82    int                         fontLeading;
83
84    // Holds the index of the item used in the previous operation (selectItem, deselectItem)
85    // Adding of an item or clearing of the list sets this index to -1
86    // The index is used at the moment of the post of ACTION_PERFORMED event after the mouse double click event.
87    int                         currentIndex = -1;
88
89    // Used for tracking selection/deselection between mousePress/Release
90    // and for ItemEvents
91    int                         eventIndex = -1;
92    int                         eventType = NONE;
93
94    // Holds the index of the item that receive focus
95    // This variable is reasonable only for multiple list
96    // since 'focusIndex' and 'selected[0]' are equal for single-selection list
97    int                         focusIndex;
98
99    int                         maxLength;
100    boolean                     vsbVis;  // visibility of scrollbars
101    boolean                     hsbVis;
102    int                         listWidth;  // Width of list portion of List
103    int                         listHeight; // Height of list portion of List
104    // (i.e. without scrollbars)
105
106    private int firstTimeVisibleIndex = 0;
107
108    // Motif Lists don't seem to inherit the background color from their
109    // parent when an app is first started up.  So, we track if the colors have
110    // been set.  See getListBackground()/getListForeground().
111    boolean bgColorSet;
112    boolean fgColorSet;
113
114    // Holds the true if mouse is dragging outside of the area of the list
115    // The flag is used at the moment of the dragging and releasing mouse
116    // See 6243382 for more information
117    boolean mouseDraggedOutHorizontally = false;
118    boolean mouseDraggedOutVertically = false;
119
120    // Holds the true if a mouse event was originated on the scrollbar
121    // See 6300527 for more information
122    boolean isScrollBarOriginated = false;
123
124    // This variable is set to true after the "mouse pressed" event and to false after the "mouse released" event
125    // Fixed 6293432: Key events ('SPACE', 'UP', 'DOWN') aren't blocked if mouse is kept in 'PRESSED' state for List, XAWT
126    boolean isMousePressed = false;
127
128    /**
129     * Create a list
130     */
131    XListPeer(List target) {
132        super(target);
133    }
134
135    /**
136     * Overridden from XWindow
137     */
138    public void preInit(XCreateWindowParams params) {
139        super.preInit(params);
140
141        // Stuff that must be initialized before layout() is called
142        items = new Vector<>();
143        createVerScrollbar();
144        createHorScrollbar();
145
146        painter = new ListPainter();
147
148        // See 6246467 for more information
149        bgColorSet = target.isBackgroundSet();
150        fgColorSet = target.isForegroundSet();
151    }
152
153    public void postInit(XCreateWindowParams params) {
154        super.postInit(params);
155        initFontMetrics();
156        // TODO: more efficient way?
157        //       do we really want/need a copy of all the items?
158        // get all items from target
159        List l = (List)target;
160        int stop = l.getItemCount();
161        for (int i = 0 ; i < stop; i++) {
162            items.addElement(l.getItem(i));
163        }
164
165        /* make the visible position visible. */
166        int index = l.getVisibleIndex();
167        if (index >= 0) {
168            // Can't call makeVisible since it check scroll bar,
169            // initialize scroll bar instead
170            vsb.setValues(index, 0, 0, items.size());
171        }
172
173        // NOTE: needs to have target set
174        maxLength = maxLength();
175
176        // get the index containing all indexes to selected items
177        int sel[] = l.getSelectedIndexes();
178        selected = new int[sel.length];
179        // TODO: shouldn't this be arraycopy()?
180        for (int i = 0 ; i < sel.length ; i ++) {
181            selected[i] = sel[i];
182        }
183        // The select()ed item should become the focused item, but we don't
184        // get the select() call because the peer generally hasn't yet been
185        // created during app initialization.
186        // TODO: For multi-select lists, it should be the highest selected index
187        if (sel.length > 0) {
188            setFocusIndex(sel[sel.length - 1]);
189        }
190        else {
191            setFocusIndex(0);
192        }
193
194        multipleSelections = l.isMultipleMode();
195    }
196
197
198    /**
199     * add Vertical Scrollbar
200     */
201    void createVerScrollbar() {
202        vsb = new XVerticalScrollbar(this);
203        vsb.setValues(0, 0, 0, 0, 1, 1);
204    }
205
206
207    /**
208     * add Horizontal scrollbar
209     */
210    void createHorScrollbar() {
211        hsb = new XHorizontalScrollbar(this);
212        hsb.setValues(0, 0, 0, 0, HORIZ_SCROLL_AMT, HORIZ_SCROLL_AMT);
213    }
214
215    /* New method name for 1.1 */
216    public void add(String item, int index) {
217        addItem(item, index);
218    }
219
220    /* New method name for 1.1 */
221    public void removeAll() {
222        clear();
223        maxLength = 0;
224    }
225
226    /* New method name for 1.1 */
227    public void setMultipleMode (boolean b) {
228        setMultipleSelections(b);
229    }
230
231    /* New method name for 1.1 */
232    public Dimension getPreferredSize(int rows) {
233        return preferredSize(rows);
234    }
235
236    /* New method name for 1.1 */
237    public Dimension getMinimumSize(int rows) {
238        return minimumSize(rows);
239    }
240
241    /**
242     * Minimum size.
243     */
244    public Dimension minimumSize() {
245        return minimumSize(DEFAULT_VISIBLE_ROWS);
246    }
247
248    /**
249     * return the preferredSize
250     */
251    public Dimension preferredSize(int v) {
252        return minimumSize(v);
253    }
254
255    /**
256     * return the minimumsize
257     */
258    public Dimension minimumSize(int v) {
259        FontMetrics fm = getFontMetrics(getFont());
260        initFontMetrics();
261        return new Dimension(20 + fm.stringWidth("0123456789abcde"),
262                             getItemHeight() * v + (2*MARGIN));
263    }
264
265    /**
266     * Calculate font metrics
267     */
268    void initFontMetrics() {
269        FontMetrics fm = getFontMetrics(getFont());
270        fontHeight = fm.getHeight();
271        fontAscent = fm.getAscent();
272        fontLeading = fm.getLeading();
273    }
274
275
276    /**
277     * return the length of the largest item in the list
278     */
279    int maxLength() {
280        FontMetrics fm = getFontMetrics(getFont());
281        int m = 0;
282        int end = items.size();
283        for(int i = 0 ; i < end ; i++) {
284            int l = fm.stringWidth(items.elementAt(i));
285            m = Math.max(m, l);
286        }
287        return m;
288    }
289
290    /**
291     * Calculates the width of item's label
292     */
293    int getItemWidth(int i) {
294        FontMetrics fm = getFontMetrics(getFont());
295        return fm.stringWidth(items.elementAt(i));
296    }
297
298    /**
299     * return the on-screen width of the given string "str"
300     */
301    int stringLength(String str) {
302        FontMetrics fm = getFontMetrics(target.getFont());
303        return fm.stringWidth(str);
304    }
305
306    public void setForeground(Color c) {
307        fgColorSet = true;
308        super.setForeground(c);
309    }
310
311    public void setBackground(Color c) {
312        bgColorSet = true;
313        super.setBackground(c);
314    }
315
316    /**
317     * Returns the color that should be used to paint the background of
318     * the list of items.  Note that this is not the same as
319     * target.getBackground() which is the color of the scrollbars, and the
320     * lower-right corner of the Component when the scrollbars are displayed.
321     */
322    private Color getListBackground(Color[] colors) {
323        if (bgColorSet) {
324            return colors[BACKGROUND_COLOR];
325        }
326        else {
327            return SystemColor.text;
328        }
329    }
330
331    /**
332     * Returns the color that should be used to paint the list item text.
333     */
334    private Color getListForeground(Color[] colors) {
335        if (fgColorSet) {
336            return colors[FOREGROUND_COLOR];
337        }
338        else {
339            return SystemColor.textText;
340        }
341    }
342
343    Rectangle getVScrollBarRec() {
344        return new Rectangle(width - (SCROLLBAR_WIDTH), 0, SCROLLBAR_WIDTH+1, height);
345    }
346
347    Rectangle getHScrollBarRec() {
348        return new Rectangle(0, height - SCROLLBAR_WIDTH, width, SCROLLBAR_WIDTH);
349    }
350
351    int getFirstVisibleItem() {
352        if (vsbVis) {
353            return vsb.getValue();
354        } else {
355            return 0;
356        }
357    }
358
359    int getLastVisibleItem() {
360        if (vsbVis) {
361            return Math.min(items.size()-1, vsb.getValue() + itemsInWindow() -1);
362        } else {
363            return Math.min(items.size()-1, itemsInWindow()-1);
364        }
365    }
366    public void repaintScrollbarRequest(XScrollbar scrollbar) {
367        if (scrollbar == hsb)  {
368            repaint(PAINT_HSCROLL);
369        }
370        else if (scrollbar == vsb) {
371            repaint(PAINT_VSCROLL);
372        }
373    }
374    /**
375     * Overridden for performance
376     */
377    public void repaint() {
378        repaint(getFirstVisibleItem(), getLastVisibleItem(), PAINT_ALL);
379    }
380
381    private void repaint(int options) {
382        repaint(getFirstVisibleItem(), getLastVisibleItem(), options);
383    }
384
385    private void repaint(int firstItem, int lastItem, int options) {
386        repaint(firstItem, lastItem, options, null, null);
387    }
388
389    /**
390     * In most cases the entire area of the component doesn't have
391     * to be repainted. The method repaints the particular areas of
392     * the component. The areas to repaint is specified by the option
393     * parameter. The possible values of the option parameter are:
394     * PAINT_VSCROLL, PAINT_HSCROLL, PAINT_ITEMS, PAINT_FOCUS,
395     * PAINT_HIDEFOCUS, PAINT_BACKGROUND, PAINT_ALL, COPY_AREA.
396     *
397     * Note that the COPY_AREA value initiates copy of a source area
398     * of the component by a distance by means of the copyArea method
399     * of the Graphics class.
400     *
401     * @param firstItem the position of the first item of the range to repaint
402     * @param lastItem the position of the last item of the range to repaint
403     * @param options specifies the particular area of the component to repaint
404     * @param source the area of the component to copy
405     * @param distance the distance to copy the source area
406     */
407    private void repaint(int firstItem, int lastItem, int options, Rectangle source, Point distance) {
408        final Graphics g = getGraphics();
409        if (g != null) {
410            try {
411                painter.paint(g, firstItem, lastItem, options, source, distance);
412                postPaintEvent(target, 0, 0, getWidth(), getHeight());
413            } finally {
414                g.dispose();
415            }
416        }
417    }
418    @Override
419    void paintPeer(final Graphics g) {
420        painter.paint(g, getFirstVisibleItem(), getLastVisibleItem(), PAINT_ALL);
421    }
422    public boolean isFocusable() { return true; }
423
424    // TODO: share/promote the Focus methods?
425    public void focusGained(FocusEvent e) {
426        super.focusGained(e);
427        repaint(PAINT_FOCUS);
428    }
429
430    public void focusLost(FocusEvent e) {
431        super.focusLost(e);
432        repaint(PAINT_FOCUS);
433    }
434
435    /**
436     * Layout the sub-components of the List - that is, the scrollbars and the
437     * list of items.
438     */
439    public void layout() {
440        int vis, maximum;
441        boolean vsbWasVisible;
442        int origVSBVal;
443        assert(target != null);
444
445        // Start with assumption there is not a horizontal scrollbar,
446        // see if we need a vertical scrollbar
447
448        // Bug: If the list DOES have a horiz scrollbar and the value is set to
449        // the very bottom value, value is reset in setValues() because it isn't
450        // a valid value for cases when the list DOESN'T have a horiz scrollbar.
451        // This is currently worked-around with origVSGVal.
452        origVSBVal = vsb.getValue();
453        vis = itemsInWindow(false);
454        maximum = items.size() < vis ? vis : items.size();
455        vsb.setValues(vsb.getValue(), vis, vsb.getMinimum(), maximum);
456        vsbVis = vsbWasVisible = vsbIsVisible(false);
457        listHeight = height;
458
459        // now see if we need a horizontal scrollbar
460        listWidth = getListWidth();
461        vis = listWidth - ((2 * SPACE) + (2 * MARGIN));
462        maximum = maxLength < vis ? vis : maxLength;
463        hsb.setValues(hsb.getValue(), vis, hsb.getMinimum(), maximum);
464        hsbVis = hsbIsVisible(vsbVis);
465
466        if (hsbVis) {
467            // do need a horizontal scrollbar, so recalculate height of
468            // vertical s crollbar
469            listHeight = height - SCROLLBAR_AREA;
470            vis = itemsInWindow(true);
471            maximum = items.size() < vis ? vis : items.size();
472            vsb.setValues(origVSBVal, vis, vsb.getMinimum(), maximum);
473            vsbVis = vsbIsVisible(true);
474        }
475
476        // now check to make sure we haven't changed need for vertical
477        // scrollbar - if we have, we need to
478        // recalculate horizontal scrollbar width - then we're done...
479        if (vsbWasVisible != vsbVis) {
480            listWidth = getListWidth();
481            vis = listWidth - ((2 * SPACE) + (2 * MARGIN));
482            maximum = maxLength < vis ? 0 : maxLength;
483            hsb.setValues(hsb.getValue(), vis, hsb.getMinimum(), maximum);
484            hsbVis = hsbIsVisible(vsbVis);
485        }
486
487        vsb.setSize(SCROLLBAR_WIDTH, listHeight);
488        hsb.setSize(listWidth, SCROLLBAR_WIDTH);
489
490        vsb.setBlockIncrement(itemsInWindow());
491        hsb.setBlockIncrement(width - ((2 * SPACE) + (2 * MARGIN) + (vsbVis ? SCROLLBAR_AREA : 0)));
492    }
493
494    int getItemWidth() {
495        return width - ((2 * MARGIN) + (vsbVis ? SCROLLBAR_AREA : 0));
496    }
497
498    /* Returns height of an item in the list */
499    int getItemHeight() {
500        return (fontHeight - fontLeading) + (2*SPACE);
501    }
502
503    int getItemX() {
504        return MARGIN + SPACE;
505    }
506
507    int getItemY(int item) {
508        return index2y(item);
509    }
510
511    int getFocusIndex() {
512        return focusIndex;
513    }
514
515    void setFocusIndex(int value) {
516        focusIndex = value;
517    }
518
519    /**
520     * Update and return the focus rectangle.
521     * Focus is around the focused item, if it is visible, or
522     * around the border of the list if the focused item is scrolled off the top
523     * or bottom of the list.
524     */
525    Rectangle getFocusRect() {
526        Rectangle focusRect = new Rectangle();
527        // width is always only based on presence of vert sb
528        focusRect.x = 1;
529        focusRect.width = getListWidth() - 3;
530        // if focused item is not currently displayed in the list,  paint
531        // focus around entire list (not including scrollbars)
532        if (isIndexDisplayed(getFocusIndex())) {
533            // focus rect is around the item
534            focusRect.y = index2y(getFocusIndex()) - 2;
535            focusRect.height = getItemHeight()+1;
536        } else {
537            // focus rect is around the list
538            focusRect.y = 1;
539            focusRect.height = hsbVis ? height - SCROLLBAR_AREA : height;
540            focusRect.height -= 3;
541        }
542        return focusRect;
543    }
544
545    public void handleConfigureNotifyEvent(XEvent xev) {
546        super.handleConfigureNotifyEvent(xev);
547
548        // Update buffer
549        painter.invalidate();
550    }
551    public boolean handlesWheelScrolling() { return true; }
552
553    // FIXME: need to support MouseWheel scrolling, too
554    void handleJavaMouseEvent(MouseEvent e) {
555        super.handleJavaMouseEvent(e);
556        int i = e.getID();
557        switch (i) {
558          case MouseEvent.MOUSE_PRESSED:
559              mousePressed(e);
560              break;
561          case MouseEvent.MOUSE_RELEASED:
562              mouseReleased(e);
563              break;
564          case MouseEvent.MOUSE_DRAGGED:
565              mouseDragged(e);
566              break;
567        }
568    }
569
570    void handleJavaMouseWheelEvent(MouseWheelEvent e) {
571        if (ListHelper.doWheelScroll(vsbVis ? vsb : null,
572                                     hsbVis ? hsb : null, e)) {
573            repaint();
574        }
575    }
576    @SuppressWarnings("deprecation")
577    void mousePressed(MouseEvent mouseEvent) {
578        if (log.isLoggable(PlatformLogger.Level.FINER)) {
579            log.finer(mouseEvent.toString() + ", hsb " + hsbVis + ", vsb " + vsbVis);
580        }
581        if (isEnabled() && mouseEvent.getButton() == MouseEvent.BUTTON1) {
582            if (inWindow(mouseEvent.getX(), mouseEvent.getY())) {
583                if (log.isLoggable(PlatformLogger.Level.FINE)) {
584                    log.fine("Mouse press in items area");
585                }
586                active = WINDOW;
587                int i = y2index(mouseEvent.getY());
588                if (i >= 0) {
589                    if (multipleSelections) {
590                        if (isSelected(i)) {
591                            // See 6243382 for more information
592                            deselectItem(i);
593                            eventIndex = i;
594                            eventType = ItemEvent.DESELECTED;
595                        }
596                        else {
597                            selectItem(i);
598                            eventIndex = i;
599                            eventType = ItemEvent.SELECTED;
600                        }
601                    }
602                    // Backward-compatible bug: even if a single-select
603                    // item is already selected, we send an ITEM_STATE_CHANGED/
604                    // SELECTED event.  Engineer's Toolbox appears to rely on
605                    // this.
606                    //else if (!isSelected(i)) {
607                    else {
608                        selectItem(i);
609                        eventIndex = i;
610                        eventType = ItemEvent.SELECTED;
611                    }
612                    // Restoring Windows behaviour
613                    // We should update focus index after "mouse pressed" event
614                    setFocusIndex(i);
615                    repaint(PAINT_FOCUS);
616                } else {
617                    // 6426186: reset variable to prevent action event
618                    // if user clicks on unoccupied area of list
619                    currentIndex = -1;
620                }
621            } else if (inVerticalScrollbar(mouseEvent.getX(), mouseEvent.getY())) {
622                if (log.isLoggable(PlatformLogger.Level.FINE)) {
623                    log.fine("Mouse press in vertical scrollbar");
624                }
625                active = VERSCROLLBAR;
626                vsb.handleMouseEvent(mouseEvent.getID(),
627                                     mouseEvent.getModifiers(),
628                                     mouseEvent.getX() - (width - SCROLLBAR_WIDTH),
629                                     mouseEvent.getY());
630            } else if (inHorizontalScrollbar(mouseEvent.getX(), mouseEvent.getY())) {
631                if (log.isLoggable(PlatformLogger.Level.FINE)) {
632                    log.fine("Mouse press in horizontal scrollbar");
633                }
634                active = HORSCROLLBAR;
635                hsb.handleMouseEvent(mouseEvent.getID(),
636                                     mouseEvent.getModifiers(),
637                                     mouseEvent.getX(),
638                                     mouseEvent.getY() - (height - SCROLLBAR_WIDTH));
639
640            }
641            isMousePressed = true;
642        }
643    }
644    @SuppressWarnings("deprecation")
645    void mouseReleased(MouseEvent mouseEvent) {
646        if (isEnabled() && mouseEvent.getButton() == MouseEvent.BUTTON1) {
647            //winReleaseCursorFocus();
648            int clickCount = mouseEvent.getClickCount();
649            if (active == VERSCROLLBAR) {
650                vsb.handleMouseEvent(mouseEvent.getID(),
651                                     mouseEvent.getModifiers(),
652                                     mouseEvent.getX()-(width-SCROLLBAR_WIDTH),
653                                     mouseEvent.getY());
654            } else if(active == HORSCROLLBAR) {
655                hsb.handleMouseEvent(mouseEvent.getID(),
656                                     mouseEvent.getModifiers(),
657                                     mouseEvent.getX(),
658                                     mouseEvent.getY()-(height-SCROLLBAR_WIDTH));
659            } else if ( ( currentIndex >= 0 ) && ( clickCount >= 2 ) &&
660                        ( clickCount % 2 == 0 ) ) {
661                postEvent(new ActionEvent(target,
662                                          ActionEvent.ACTION_PERFORMED,
663                                          items.elementAt(currentIndex),
664                                          mouseEvent.getWhen(),
665                                          mouseEvent.getModifiers()));  // No ext mods
666            } else if (active == WINDOW) {
667                // See 6243382 for more information
668                trackMouseReleasedScroll();
669
670                if (eventType == ItemEvent.DESELECTED) {
671                    assert multipleSelections : "Shouldn't get a deselect for a single-select List";
672                    // Paint deselection the release
673                    deselectItem(eventIndex);
674                }
675                if (eventType != NONE) {
676                    postEvent(new ItemEvent((List)target,
677                                ItemEvent.ITEM_STATE_CHANGED,
678                                Integer.valueOf(eventIndex),
679                                eventType));
680                }
681            }
682            active = NONE;
683            eventIndex = -1;
684            eventType = NONE;
685            isMousePressed = false;
686        }
687    }
688
689    @SuppressWarnings("deprecation")
690    void mouseDragged(MouseEvent mouseEvent) {
691        // TODO: can you drag w/ any other buttons?  what about multiple buttons?
692        if (isEnabled() &&
693            (mouseEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) {
694            if ((active == VERSCROLLBAR)) {
695                vsb.handleMouseEvent(mouseEvent.getID(),
696                                     mouseEvent.getModifiers(),
697                                     mouseEvent.getX()-(width-SCROLLBAR_WIDTH),
698                                     mouseEvent.getY());
699            } else if ((active == HORSCROLLBAR)) {
700                hsb.handleMouseEvent(mouseEvent.getID(),
701                                     mouseEvent.getModifiers(),
702                                     mouseEvent.getX(),
703                                     mouseEvent.getY()-(height-SCROLLBAR_WIDTH));
704            } else if (active == WINDOW) {
705                int i = y2index(mouseEvent.getY());
706                if (multipleSelections) {
707                    // Multi-select only:
708                    // If a selected item was pressed on and then dragged off
709                    // of, cancel the pending deselect.
710                    if (eventType == ItemEvent.DESELECTED) {
711                        if (i != eventIndex) {
712                            eventType = NONE;
713                            eventIndex = -1;
714                        }
715                    }
716                }
717                else if (eventType == ItemEvent.SELECTED) {
718                    // Single-select only:
719                    // If an unselected item was pressed on, track the drag
720                    // and select the item under the mouse
721
722                    // See 6243382 for more information
723                    trackMouseDraggedScroll(mouseEvent);
724
725                    if (i >= 0 && !isSelected(i)) {
726                        int oldSel = eventIndex;
727                        selectItem(i);
728                        eventIndex = i;
729                        repaint(oldSel, eventIndex, PAINT_ITEMS);
730                    }
731                }
732                // Restoring Windows behaviour
733                // We should update focus index after "mouse dragged" event
734                if (i >= 0) {
735                    setFocusIndex(i);
736                    repaint(PAINT_FOCUS);
737                }
738            }
739        }
740    }
741
742    /*
743     * Helper method for XListPeer with integrated vertical scrollbar.
744     * Start or stop vertical scrolling when mouse dragged in / out the area of the list if it's required
745     * Restoring Motif behavior
746     * See 6243382 for more information
747     */
748    void trackMouseDraggedScroll(MouseEvent mouseEvent){
749
750        if (vsb.beforeThumb(mouseEvent.getX(), mouseEvent.getY())) {
751            vsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
752        } else {
753            vsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
754        }
755
756        if(mouseEvent.getY() < 0 || mouseEvent.getY() >= listHeight){
757            if (!mouseDraggedOutVertically){
758                mouseDraggedOutVertically = true;
759                vsb.startScrollingInstance();
760            }
761        }else{
762            if (mouseDraggedOutVertically){
763                mouseDraggedOutVertically = false;
764                vsb.stopScrollingInstance();
765            }
766        }
767
768        if (hsb.beforeThumb(mouseEvent.getX(), mouseEvent.getY())) {
769            hsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
770        } else {
771            hsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
772        }
773
774        if (mouseEvent.getX() < 0 || mouseEvent.getX() >= listWidth) {
775            if (!mouseDraggedOutHorizontally){
776                mouseDraggedOutHorizontally = true;
777                hsb.startScrollingInstance();
778            }
779        }else{
780            if (mouseDraggedOutHorizontally){
781                mouseDraggedOutHorizontally = false;
782                hsb.stopScrollingInstance();
783            }
784        }
785    }
786
787    /*
788     * Helper method for XListPeer with integrated vertical scrollbar.
789     * Stop vertical scrolling when mouse released in / out the area of the list if it's required
790     * Restoring Motif behavior
791     * see 6243382 for more information
792     */
793    void trackMouseReleasedScroll(){
794
795        if (mouseDraggedOutVertically){
796            mouseDraggedOutVertically = false;
797            vsb.stopScrollingInstance();
798        }
799
800        if (mouseDraggedOutHorizontally){
801            mouseDraggedOutHorizontally = false;
802            hsb.stopScrollingInstance();
803        }
804    }
805
806    void handleJavaKeyEvent(KeyEvent e) {
807        switch(e.getID()) {
808          case KeyEvent.KEY_PRESSED:
809              if (!isMousePressed){
810                  keyPressed(e);
811              }
812              break;
813        }
814    }
815    @SuppressWarnings("deprecation")
816    void keyPressed(KeyEvent e) {
817        int keyCode = e.getKeyCode();
818        if (log.isLoggable(PlatformLogger.Level.FINE)) {
819            log.fine(e.toString());
820        }
821        switch(keyCode) {
822          case KeyEvent.VK_UP:
823          case KeyEvent.VK_KP_UP: // TODO: I assume we also want this, too
824              if (getFocusIndex() > 0) {
825                  setFocusIndex(getFocusIndex()-1);
826                  repaint(PAINT_HIDEFOCUS);
827                  // If single-select, select the item
828                  if (!multipleSelections) {
829                      selectItem(getFocusIndex());
830                      postEvent(new ItemEvent((List)target,
831                                              ItemEvent.ITEM_STATE_CHANGED,
832                                              Integer.valueOf(getFocusIndex()),
833                                              ItemEvent.SELECTED));
834                  }
835                  if (isItemHidden(getFocusIndex())) {
836                      makeVisible(getFocusIndex());
837                  }
838                  else {
839                      repaint(PAINT_FOCUS);
840                  }
841              }
842              break;
843          case KeyEvent.VK_DOWN:
844          case KeyEvent.VK_KP_DOWN: // TODO: I assume we also want this, too
845              if (getFocusIndex() < items.size() - 1) {
846                  setFocusIndex(getFocusIndex()+1);
847                  repaint(PAINT_HIDEFOCUS);
848                  // If single-select, select the item
849                  if (!multipleSelections) {
850                      selectItem(getFocusIndex());
851                      postEvent(new ItemEvent((List)target,
852                                              ItemEvent.ITEM_STATE_CHANGED,
853                                              Integer.valueOf(getFocusIndex()),
854                                              ItemEvent.SELECTED));
855                  }
856                  if (isItemHidden(getFocusIndex())) {
857                      makeVisible(getFocusIndex());
858                  }
859                  else {
860                      repaint(PAINT_FOCUS);
861                  }
862              }
863              break;
864          case KeyEvent.VK_PAGE_UP: {
865              // Assumes that scrollbar does its own bounds-checking
866              int previousValue = vsb.getValue();
867              vsb.setValue(vsb.getValue() - vsb.getBlockIncrement());
868              int currentValue = vsb.getValue();
869              // 6190768 pressing pg-up on AWT multiple selection lists the items but no item event is triggered, on XToolkit
870              // Restoring Motif behavior
871              if (previousValue!=currentValue) {
872                  setFocusIndex(Math.max(getFocusIndex()-itemsInWindow(), 0));
873                  if (!multipleSelections){
874                      selectItem(getFocusIndex());
875                      postEvent(new ItemEvent((List)target,
876                                              ItemEvent.ITEM_STATE_CHANGED,
877                                              Integer.valueOf(getFocusIndex()),
878                                              ItemEvent.SELECTED));
879                  }
880              }
881              repaint();
882              break;
883          }
884          case KeyEvent.VK_PAGE_DOWN: {
885              // Assumes that scrollbar does its own bounds-checking
886              int previousValue = vsb.getValue();
887              vsb.setValue(vsb.getValue() + vsb.getBlockIncrement());
888              int currentValue = vsb.getValue();
889              // 6190768 pressing pg-down on AWT multiple selection list selects the items but no item event is triggered, on XToolkit
890              // Restoring Motif behavior
891              if (previousValue!=currentValue) {
892                  setFocusIndex(Math.min(getFocusIndex() + itemsInWindow(), items.size()-1));
893                  if (!multipleSelections){
894                      selectItem(getFocusIndex());
895                      postEvent(new ItemEvent((List)target,
896                                              ItemEvent.ITEM_STATE_CHANGED,
897                                              Integer.valueOf(getFocusIndex()),
898                                              ItemEvent.SELECTED));
899                  }
900              }
901              repaint();
902              break;
903          }
904          case KeyEvent.VK_LEFT:
905          case KeyEvent.VK_KP_LEFT:
906              if (hsbVis & hsb.getValue() > 0) {
907                  hsb.setValue(hsb.getValue() - HORIZ_SCROLL_AMT);
908                  repaint();
909              }
910              break;
911          case KeyEvent.VK_RIGHT:
912          case KeyEvent.VK_KP_RIGHT:
913              if (hsbVis) { // Should check if already at end
914                  hsb.setValue(hsb.getValue() + HORIZ_SCROLL_AMT);
915                  repaint();
916              }
917              break;
918          // 6190778 CTRL + HOME, CTRL + END keys do not work properly for list on XToolkit
919          // Restoring Motif behavior
920          case KeyEvent.VK_HOME:
921              if (!e.isControlDown() || ((List)target).getItemCount() <= 0)
922                  break;
923              if (vsbVis) {
924                  vsb.setValue(vsb.getMinimum());
925              }
926              setFocusIndex(0);
927              if (!multipleSelections) {
928                  selectItem(getFocusIndex());
929                  postEvent(new ItemEvent((List)target,
930                                          ItemEvent.ITEM_STATE_CHANGED,
931                                          Integer.valueOf(getFocusIndex()),
932                                          ItemEvent.SELECTED));
933              }
934              repaint();
935              break;
936          case KeyEvent.VK_END:
937              if (!e.isControlDown() || ((List)target).getItemCount() <= 0)
938                  break;
939              if (vsbVis) {
940                  vsb.setValue(vsb.getMaximum());
941              }
942              setFocusIndex(items.size()-1);
943              if (!multipleSelections) {
944                  selectItem(getFocusIndex());
945                  postEvent(new ItemEvent((List)target,
946                                          ItemEvent.ITEM_STATE_CHANGED,
947                                          Integer.valueOf(getFocusIndex()),
948                                          ItemEvent.SELECTED));
949              }
950              repaint();
951              break;
952          case KeyEvent.VK_SPACE:
953              // Fixed 6299853: XToolkit: Pressing space triggers ItemStateChanged event after List.removeAll called
954              // If getFocusIndex() is less than 0, the event will not be triggered when space pressed
955              if (getFocusIndex() < 0 || ((List)target).getItemCount() <= 0) {
956                  break;
957              }
958
959              boolean isSelected = isSelected(getFocusIndex());
960
961              // Spacebar only deselects for multi-select Lists
962              if (multipleSelections && isSelected) {
963                  deselectItem(getFocusIndex());
964                  postEvent(new ItemEvent((List)target,
965                                          ItemEvent.ITEM_STATE_CHANGED,
966                                          Integer.valueOf(getFocusIndex()),
967                                          ItemEvent.DESELECTED));
968              }
969              else if (!isSelected) { // Note: this changes the Solaris/Linux
970                  // behavior to match that of win32.
971                  // That is, pressing space bar on a
972                  // single-select list when the focused
973                  // item is already selected does NOT
974                  // send an ItemEvent.SELECTED event.
975                  selectItem(getFocusIndex());
976                  postEvent(new ItemEvent((List)target,
977                                          ItemEvent.ITEM_STATE_CHANGED,
978                                          Integer.valueOf(getFocusIndex()),
979                                          ItemEvent.SELECTED));
980              }
981              break;
982          case KeyEvent.VK_ENTER:
983              // It looks to me like there are bugs as well as inconsistencies
984              // in the way the Enter key is handled by both Solaris and Windows.
985              // So for now in XAWT, I'm going to simply go by what the List docs
986              // say: "AWT also generates an action event when the user presses
987              // the return key while an item in the list is selected."
988              if (selected.length > 0) {
989                  postEvent(new ActionEvent((List)target,
990                                            ActionEvent.ACTION_PERFORMED,
991                                            items.elementAt(getFocusIndex()),
992                                            e.getWhen(),
993                                            e.getModifiers()));  // ActionEvent doesn't have
994                  // extended modifiers.
995              }
996              break;
997        }
998    }
999
1000    /**
1001     * return value from the scrollbar
1002     */
1003    public void notifyValue(XScrollbar obj, int type, int v, boolean isAdjusting) {
1004
1005        if (log.isLoggable(PlatformLogger.Level.FINE)) {
1006            log.fine("Notify value changed on " + obj + " to " + v);
1007        }
1008        int value = obj.getValue();
1009        if (obj == vsb) {
1010            scrollVertical(v - value);
1011
1012            // See 6243382 for more information
1013            int oldSel = eventIndex;
1014            int newSel = eventIndex+v-value;
1015            if (mouseDraggedOutVertically && !isSelected(newSel)){
1016                selectItem(newSel);
1017                eventIndex = newSel;
1018                repaint(oldSel, eventIndex, PAINT_ITEMS);
1019                // Scrolling select() should also set the focus index
1020                // Otherwise, the updating of the 'focusIndex' variable will be incorrect
1021                // if user drag mouse out of the area of the list
1022                setFocusIndex(newSel);
1023                repaint(PAINT_FOCUS);
1024            }
1025
1026        } else if ((XHorizontalScrollbar)obj == hsb) {
1027            scrollHorizontal(v - value);
1028        }
1029
1030    }
1031
1032    /**
1033     * deselect all items in List
1034     */
1035    private void deselectAllItems() {
1036        selected = new int [0];
1037        repaint(PAINT_ITEMS);
1038    }
1039
1040    /**
1041     * set multiple selections
1042     */
1043    public void setMultipleSelections(boolean v) {
1044        if (multipleSelections != v) {
1045            if ( !v) {
1046                int selPos = ( isSelected( focusIndex )) ? focusIndex: -1;
1047                deselectAllItems();
1048                if (selPos != -1){
1049                    selectItem(selPos);
1050                }
1051            }
1052            multipleSelections = v;
1053        }
1054    }
1055
1056    /**
1057     * add an item
1058     * if the index of the item is < 0 or >= than items.size()
1059     * then add the item to the end of the list
1060     */
1061    public void addItem(String item, int i) {
1062        int oldMaxLength = maxLength;
1063        boolean hsbWasVis = hsbVis;
1064        boolean vsbWasVis = vsbVis;
1065
1066        int addedIndex = 0; // Index where the new item ended up
1067        if (i < 0 || i >= items.size()) {
1068            i = -1;
1069        }
1070
1071        // Why we set this variable to -1 in spite of the fact that selected[] is changed in other way?
1072        // It's not clear how to reproduce incorrect behaviour based on this assignment
1073        // since before using this variable (mouseReleased) we certainly update it to correct value
1074        // So we don't modify this behaviour now
1075        currentIndex = -1;
1076
1077        if (i == -1) {
1078            items.addElement(item);
1079            i = 0;              // fix the math for the paintItems test
1080            addedIndex = items.size() - 1;
1081        } else {
1082            items.insertElementAt(item, i);
1083            addedIndex = i;
1084            for (int j = 0 ; j < selected.length ; j++) {
1085                if (selected[j] >= i) {
1086                    selected[j] += 1;
1087                }
1088            }
1089        }
1090        if (log.isLoggable(PlatformLogger.Level.FINER)) {
1091            log.finer("Adding item '" + item + "' to " + addedIndex);
1092        }
1093
1094        // Update maxLength
1095        boolean repaintItems = !isItemHidden(addedIndex);
1096        maxLength = Math.max(maxLength, getItemWidth(addedIndex));
1097        layout();
1098
1099        int options = 0;
1100        if (vsbVis != vsbWasVis || hsbVis != hsbWasVis) {
1101            // Scrollbars are being added or removed, so we must repaint all
1102            options = PAINT_ALL;
1103        }
1104        else {
1105            options = (repaintItems ? (PAINT_ITEMS):0)
1106                | ((maxLength != oldMaxLength || (hsbWasVis ^ hsbVis))?(PAINT_HSCROLL):0)
1107                | ((vsb.needsRepaint())?(PAINT_VSCROLL):0);
1108
1109        }
1110        if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1111            log.finest("Last visible: " + getLastVisibleItem() +
1112            ", hsb changed : " + (hsbWasVis ^ hsbVis) + ", items changed " + repaintItems);
1113        }
1114        repaint(addedIndex, getLastVisibleItem(), options);
1115    }
1116
1117    /**
1118     * delete items starting with s (start position) to e (end position) including s and e
1119     * if s < 0 then s = 0
1120     * if e >= items.size() then e = items.size() - 1
1121     */
1122    public void delItems(int s, int e) {
1123        // save the current state of the scrollbars
1124        boolean hsbWasVisible = hsbVis;
1125        boolean vsbWasVisible = vsbVis;
1126        int oldLastDisplayed = lastItemDisplayed();
1127
1128        if (log.isLoggable(PlatformLogger.Level.FINE)) {
1129            log.fine("Deleting from " + s + " to " + e);
1130        }
1131
1132        if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1133            log.finest("Last displayed item: " + oldLastDisplayed + ", items in window " + itemsInWindow() +
1134            ", size " + items.size());
1135        }
1136
1137        if (items.size() == 0) {
1138            return;
1139        }
1140
1141        // if user passed in flipped args, reverse them
1142        if (s > e) {
1143            int tmp = s;
1144            s = e;
1145            e = tmp;
1146        }
1147
1148        // check for starting point less than zero
1149        if (s < 0) {
1150            s = 0;
1151        }
1152
1153        // check for end point greater than the size of the list
1154        if (e >= items.size()) {
1155            e = items.size() - 1;
1156        }
1157
1158        // determine whether we're going to delete any visible elements
1159        // repaint must also be done if scrollbars appear/disappear, which
1160        // can happen from removing a non-showing list item
1161        /*
1162          boolean repaintNeeded =
1163          ((s <= lastItemDisplayed()) && (e >= vsb.getValue()));
1164        */
1165        boolean repaintNeeded = (s >= getFirstVisibleItem() && s <= getLastVisibleItem());
1166
1167        // delete the items out of the items list and out of the selected list
1168        for (int i = s ; i <= e ; i++) {
1169            items.removeElementAt(s);
1170            int j = posInSel(i);
1171            if (j != -1) {
1172                int newsel[] = new int[selected.length - 1];
1173                System.arraycopy(selected, 0, newsel, 0, j);
1174                System.arraycopy(selected, j + 1, newsel, j, selected.length - (j + 1));
1175                selected = newsel;
1176            }
1177
1178        }
1179
1180        // update the indexes in the selected array
1181        int diff = (e - s) + 1;
1182        for (int i = 0 ; i < selected.length ; i++) {
1183            if (selected[i] > e) {
1184                selected[i] -= diff;
1185            }
1186        }
1187
1188        int options = PAINT_VSCROLL;
1189        // focusedIndex updating according to native (Window, Motif) behaviour
1190        if (getFocusIndex() > e) {
1191            setFocusIndex(getFocusIndex() - (e - s + 1));
1192            options |= PAINT_FOCUS;
1193        } else if (getFocusIndex() >= s && getFocusIndex() <= e) {
1194            // Fixed 6299858: PIT. Focused border not shown on List if selected item is removed, XToolkit
1195            // We should set focus to new first item if the current first item was removed
1196            // except if the list is empty
1197            int focusBound = (items.size() > 0) ? 0 : -1;
1198            setFocusIndex(Math.max(s-1, focusBound));
1199            options |= PAINT_FOCUS;
1200        }
1201
1202        if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1203            log.finest("Multiple selections: " + multipleSelections);
1204        }
1205
1206        // update vsb.val
1207        if (vsb.getValue() >= s) {
1208            if (vsb.getValue() <= e) {
1209                vsb.setValue(e+1 - diff);
1210            } else {
1211                vsb.setValue(vsb.getValue() - diff);
1212            }
1213        }
1214
1215        int oldMaxLength = maxLength;
1216        maxLength = maxLength();
1217        if (maxLength != oldMaxLength) {
1218            // Width of the items changed affecting the range of
1219            // horizontal scrollbar
1220            options |= PAINT_HSCROLL;
1221        }
1222        layout();
1223        repaintNeeded |= (vsbWasVisible ^ vsbVis) || (hsbWasVisible ^ hsbVis); // If scrollbars visibility changed
1224        if (repaintNeeded) {
1225            options |= PAINT_ALL;
1226        }
1227        repaint(s, oldLastDisplayed, options);
1228    }
1229
1230    /**
1231     * ListPeer method
1232     */
1233    public void select(int index) {
1234        // Programmatic select() should also set the focus index
1235        setFocusIndex(index);
1236        repaint(PAINT_FOCUS);
1237        selectItem(index);
1238    }
1239
1240    /**
1241     * select the index
1242     * redraw the list to the screen
1243     */
1244    void selectItem(int index) {
1245        // NOTE: instead of recalculating and the calling repaint(), painting
1246        // is done immediately
1247
1248        // 6190746 List does not trigger ActionEvent when double clicking a programmatically selected item, XToolkit
1249        // If we invoke select(int) before setVisible(boolean), then variable currentIndex will equals -1. At the same time isSelected may be true.
1250        // Restoring Motif behavior
1251        currentIndex = index;
1252
1253        if (isSelected(index)) {
1254            return;
1255        }
1256        if (!multipleSelections) {
1257            if (selected.length == 0) { // No current selection
1258                selected = new int[1];
1259                selected[0] = index;
1260            }
1261            else {
1262                int oldSel = selected[0];
1263                selected[0] = index;
1264                if (!isItemHidden(oldSel)) {
1265                    // Only bother painting if item is visible (4895367)
1266                    repaint(oldSel, oldSel, PAINT_ITEMS);
1267                }
1268            }
1269        } else {
1270            // insert "index" into the selection array
1271            int newsel[] = new int[selected.length + 1];
1272            int i = 0;
1273            while (i < selected.length && index > selected[i]) {
1274                newsel[i] = selected[i];
1275                i++;
1276            }
1277            newsel[i] = index;
1278            System.arraycopy(selected, i, newsel, i+1, selected.length - i);
1279            selected = newsel;
1280        }
1281        if (!isItemHidden(index)) {
1282            // Only bother painting if item is visible (4895367)
1283            repaint(index, index, PAINT_ITEMS);
1284        }
1285    }
1286
1287    /**
1288     * ListPeer method
1289     * focusedIndex isn't updated according to native (Window, Motif) behaviour
1290     */
1291    public void deselect(int index) {
1292        deselectItem(index);
1293    }
1294
1295    /**
1296     * deselect the index
1297     * redraw the list to the screen
1298     */
1299    void deselectItem(int index) {
1300        if (!isSelected(index)) {
1301            return;
1302        }
1303        if (!multipleSelections) {
1304            // TODO: keep an int[0] and int[1] around and just use them instead
1305            // creating new ones all the time
1306            selected = new int[0];
1307        } else {
1308            int i = posInSel(index);
1309            int newsel[] = new int[selected.length - 1];
1310            System.arraycopy(selected, 0, newsel, 0, i);
1311            System.arraycopy(selected, i+1, newsel, i, selected.length - (i+1));
1312            selected = newsel;
1313        }
1314        currentIndex = index;
1315        if (!isItemHidden(index)) {
1316            // Only bother repainting if item is visible
1317            repaint(index, index, PAINT_ITEMS);
1318        }
1319    }
1320
1321    /**
1322     * ensure that the given index is visible, scrolling the List
1323     * if necessary, or doing nothing if the item is already visible.
1324     * The List must be repainted for changes to be visible.
1325     */
1326    public void makeVisible(int index) {
1327        if (index < 0 || index >= items.size()) {
1328            return;
1329        }
1330        if (isItemHidden(index)) {  // Do I really need to call this?
1331            // If index is above the top, scroll up
1332            if (index < vsb.getValue()) {
1333                scrollVertical(index - vsb.getValue());
1334            }
1335            // If index is below the bottom, scroll down
1336            else if (index > lastItemDisplayed()) {
1337                int val = index - lastItemDisplayed();
1338                scrollVertical(val);
1339            }
1340        }
1341    }
1342
1343    /**
1344     * clear
1345     */
1346    public void clear() {
1347        selected = new int[0];
1348        items = new Vector<>();
1349        currentIndex = -1;
1350        // Fixed 6291736: ITEM_STATE_CHANGED triggered after List.removeAll(), XToolkit
1351        // We should update 'focusIndex' variable more carefully
1352        setFocusIndex(-1);
1353        vsb.setValue(0);
1354        maxLength = 0;
1355        layout();
1356        repaint();
1357    }
1358
1359    /**
1360     * return the selected indexes
1361     */
1362    public int[] getSelectedIndexes() {
1363        return selected;
1364    }
1365
1366    /**
1367     * return the y value of the given index "i".
1368     * the y value represents the top of the text
1369     * NOTE: index can be larger than items.size as long
1370     * as it can fit the window
1371     */
1372    int index2y(int index) {
1373        int h = getItemHeight();
1374
1375        //if (index < vsb.getValue() || index > vsb.getValue() + itemsInWindow()) {
1376        return MARGIN + ((index - vsb.getValue()) * h) + SPACE;
1377    }
1378
1379    /* return true if the y is a valid y coordinate for
1380     *  a VISIBLE list item, otherwise returns false
1381     */
1382    boolean validY(int y) {
1383
1384        int shown = itemsDisplayed();
1385        int lastY = shown * getItemHeight() + MARGIN;
1386
1387        if (shown == itemsInWindow()) {
1388            lastY += MARGIN;
1389        }
1390
1391        if (y < 0 || y >= lastY) {
1392            return false;
1393        }
1394
1395        return true;
1396    }
1397
1398    /**
1399     * return the position of the index in the selected array
1400     * if the index isn't in the array selected return -1;
1401     */
1402    int posInSel(int index) {
1403        for (int i = 0 ; i < selected.length ; i++) {
1404            if (index == selected[i]) {
1405                return i;
1406            }
1407        }
1408        return -1;
1409    }
1410
1411    boolean isIndexDisplayed(int idx) {
1412        int lastDisplayed = lastItemDisplayed();
1413
1414        return idx <= lastDisplayed &&
1415            idx >= Math.max(0, lastDisplayed - itemsInWindow() + 1);
1416    }
1417
1418    /**
1419     * returns index of last item displayed in the List
1420     */
1421    int lastItemDisplayed() {
1422        int n = itemsInWindow();
1423        return (Math.min(items.size() - 1, (vsb.getValue() + n) - 1));
1424    }
1425
1426    /**
1427     * returns whether the given index is currently scrolled off the top or
1428     * bottom of the List.
1429     */
1430    boolean isItemHidden(int index) {
1431        return index < vsb.getValue() ||
1432            index >= vsb.getValue() + itemsInWindow();
1433    }
1434
1435    /**
1436     * returns the width of the list portion of the component (accounts for
1437     * presence of vertical scrollbar)
1438     */
1439    int getListWidth() {
1440        return vsbVis ? width - SCROLLBAR_AREA : width;
1441    }
1442
1443    /**
1444     * returns number of  items actually displayed in the List
1445     */
1446    int itemsDisplayed() {
1447
1448        return (Math.min(items.size()-vsb.getValue(), itemsInWindow()));
1449
1450    }
1451
1452    /**
1453     * scrollVertical
1454     * y is the number of items to scroll
1455     */
1456    void scrollVertical(int y) {
1457        if (log.isLoggable(PlatformLogger.Level.FINE)) {
1458            log.fine("Scrolling vertically by " + y);
1459        }
1460        int itemsInWin = itemsInWindow();
1461        int h = getItemHeight();
1462        int pixelsToScroll = y * h;
1463
1464        if (vsb.getValue() < -y) {
1465            y = -vsb.getValue();
1466        }
1467        vsb.setValue(vsb.getValue() + y);
1468
1469        Rectangle source = null;
1470        Point distance = null;
1471        int firstItem = 0, lastItem = 0;
1472        int options = PAINT_HIDEFOCUS | PAINT_ITEMS | PAINT_VSCROLL | PAINT_FOCUS;
1473        if (y > 0) {
1474            if (y < itemsInWin) {
1475                source = new Rectangle(MARGIN, MARGIN + pixelsToScroll, width - SCROLLBAR_AREA, h * (itemsInWin - y - 1)-1);
1476                distance = new Point(0, -pixelsToScroll);
1477                options |= COPY_AREA;
1478            }
1479            firstItem = vsb.getValue() + itemsInWin - y - 1;
1480            lastItem = vsb.getValue() + itemsInWin - 1;
1481
1482        } else if (y < 0) {
1483            if (y + itemsInWindow() > 0) {
1484                source = new Rectangle(MARGIN, MARGIN, width - SCROLLBAR_AREA, h * (itemsInWin + y));
1485                distance = new Point(0, -pixelsToScroll);
1486                options |= COPY_AREA;
1487            }
1488            firstItem = vsb.getValue();
1489            lastItem = Math.min(getLastVisibleItem(), vsb.getValue() + -y);
1490        }
1491        repaint(firstItem, lastItem, options, source, distance);
1492    }
1493
1494    /**
1495     * scrollHorizontal
1496     * x is the number of pixels to scroll
1497     */
1498    void scrollHorizontal(int x) {
1499        if (log.isLoggable(PlatformLogger.Level.FINE)) {
1500            log.fine("Scrolling horizontally by " + y);
1501        }
1502        int w = getListWidth();
1503        w -= ((2 * SPACE) + (2 * MARGIN));
1504        int h = height - (SCROLLBAR_AREA + (2 * MARGIN));
1505        hsb.setValue(hsb.getValue() + x);
1506
1507        int options = PAINT_ITEMS | PAINT_HSCROLL;
1508
1509        Rectangle source = null;
1510        Point distance = null;
1511        if (x < 0) {
1512            source = new Rectangle(MARGIN + SPACE, MARGIN, w + x, h);
1513            distance = new Point(-x, 0);
1514            options |= COPY_AREA;
1515        } else if (x > 0) {
1516            source = new Rectangle(MARGIN + SPACE + x, MARGIN, w - x, h);
1517            distance = new Point(-x, 0);
1518            options |= COPY_AREA;
1519        }
1520        repaint(vsb.getValue(), lastItemDisplayed(), options, source, distance);
1521    }
1522
1523    /**
1524     * return the index
1525     */
1526    int y2index(int y) {
1527        if (!validY(y)) {
1528            return -1;
1529        }
1530
1531        int i = (y - MARGIN) / getItemHeight() + vsb.getValue();
1532        int last = lastItemDisplayed();
1533
1534        if (i > last) {
1535            i = last;
1536        }
1537
1538        return i;
1539
1540    }
1541
1542    /**
1543     * is the index "index" selected
1544     */
1545    boolean isSelected(int index) {
1546        if (eventType == ItemEvent.SELECTED && index == eventIndex) {
1547            return true;
1548        }
1549        for (int i = 0 ; i < selected.length ; i++) {
1550            if (selected[i] == index) {
1551                return true;
1552            }
1553        }
1554        return false;
1555    }
1556
1557    /**
1558     * return the number of items that can fit
1559     * in the current window
1560     */
1561    int itemsInWindow(boolean scrollbarVisible) {
1562        int h;
1563        if (scrollbarVisible) {
1564            h = height - ((2 * MARGIN) + SCROLLBAR_AREA);
1565        } else {
1566            h = height - 2*MARGIN;
1567        }
1568        return (h / getItemHeight());
1569    }
1570
1571    int itemsInWindow() {
1572        return itemsInWindow(hsbVis);
1573    }
1574
1575    /**
1576     * return true if the x and y position is in the horizontal scrollbar
1577     */
1578    boolean inHorizontalScrollbar(int x, int y) {
1579        int w = getListWidth();
1580        int h = height - SCROLLBAR_WIDTH;
1581        return (hsbVis &&  (x >= 0) && (x <= w) && (y > h));
1582    }
1583
1584    /**
1585     * return true if the x and y position is in the verticalscrollbar
1586     */
1587    boolean inVerticalScrollbar(int x, int y) {
1588        int w = width - SCROLLBAR_WIDTH;
1589        int h = hsbVis ? height - SCROLLBAR_AREA : height;
1590        return (vsbVis && (x > w) && (y >= 0) && (y <= h));
1591    }
1592
1593    /**
1594     * return true if the x and y position is in the window
1595     */
1596    boolean inWindow(int x, int y) {
1597        int w = getListWidth();
1598        int h = hsbVis ? height - SCROLLBAR_AREA : height;
1599        return ((x >= 0) && (x <= w)) && ((y >= 0) && (y <= h));
1600    }
1601
1602    /**
1603     * return true if vertical scrollbar is visible and false otherwise;
1604     * hsbVisible is the visibility of the horizontal scrollbar
1605     */
1606    boolean vsbIsVisible(boolean hsbVisible){
1607        return (items.size() > itemsInWindow(hsbVisible));
1608    }
1609
1610    /**
1611     * return true if horizontal scrollbar is visible and false otherwise;
1612     * vsbVisible is the visibility of the vertical scrollbar
1613     */
1614    boolean hsbIsVisible(boolean vsbVisible){
1615        int w = width - ((2*SPACE) + (2*MARGIN) + (vsbVisible ? SCROLLBAR_AREA : 0));
1616        return (maxLength > w);
1617    }
1618
1619    /*
1620     * Returns true if the event has been handled and should not be
1621     * posted to Java
1622     */
1623    boolean prePostEvent(final AWTEvent e) {
1624        if (e instanceof MouseEvent) {
1625            return prePostMouseEvent((MouseEvent)e);
1626        }
1627        return super.prePostEvent(e);
1628    }
1629
1630    /*
1631     * Fixed 6240151: XToolkit: Dragging the List scrollbar initiates DnD
1632     * To be compatible with Motif, MouseEvent originated on the scrollbar
1633     * should be sent into Java in this way:
1634     * - post: MOUSE_ENTERED, MOUSE_EXITED, MOUSE_MOVED
1635     * - don't post: MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, MOUSE_DRAGGED
1636     */
1637    boolean prePostMouseEvent(final MouseEvent me){
1638        if (getToplevelXWindow().isModalBlocked()) {
1639            return false;
1640        }
1641
1642        int eventId = me.getID();
1643
1644        if (eventId == MouseEvent.MOUSE_MOVED)
1645        {
1646            // only for performance improvement
1647        }else if((eventId == MouseEvent.MOUSE_DRAGGED ||
1648                  eventId == MouseEvent.MOUSE_RELEASED) &&
1649                 isScrollBarOriginated)
1650        {
1651            if (eventId == MouseEvent.MOUSE_RELEASED) {
1652                isScrollBarOriginated = false;
1653            }
1654            handleJavaMouseEventOnEDT(me);
1655            return true;
1656        }else if ((eventId == MouseEvent.MOUSE_PRESSED ||
1657                   eventId == MouseEvent.MOUSE_CLICKED) &&
1658                  (inVerticalScrollbar(me.getX(), me.getY()) ||
1659                   inHorizontalScrollbar(me.getX(), me.getY())))
1660        {
1661            if (eventId == MouseEvent.MOUSE_PRESSED) {
1662                isScrollBarOriginated = true;
1663            }
1664            handleJavaMouseEventOnEDT(me);
1665            return true;
1666        }
1667        return false;
1668    }
1669
1670    /*
1671     * Do handleJavaMouseEvent on EDT
1672     */
1673    void handleJavaMouseEventOnEDT(final MouseEvent me){
1674        InvocationEvent ev = new InvocationEvent(target, new Runnable() {
1675            public void run() {
1676                handleJavaMouseEvent(me);
1677            }
1678        });
1679        postEvent(ev);
1680    }
1681
1682    /*
1683     * Fixed 5010944: List's rows overlap one another
1684     * The bug is due to incorrent caching of the list item size
1685     * So we should recalculate font metrics on setFont
1686     */
1687    public void setFont(Font f) {
1688        if (!Objects.equals(getFont(), f)) {
1689            super.setFont(f);
1690            initFontMetrics();
1691            layout();
1692            repaint();
1693        }
1694    }
1695
1696    /**
1697     * Sometimes painter is called on Toolkit thread, so the lock sequence is:
1698     *     awtLock -> Painter -> awtLock
1699     * Sometimes it is called on other threads:
1700     *     Painter -> awtLock
1701     * Since we can't guarantee the sequence, use awtLock.
1702     */
1703    class ListPainter {
1704        VolatileImage buffer;
1705        Color[] colors;
1706
1707        private Color getListForeground() {
1708            if (fgColorSet) {
1709                return colors[FOREGROUND_COLOR];
1710            }
1711            else {
1712            return SystemColor.textText;
1713            }
1714        }
1715        private Color getListBackground() {
1716            if (bgColorSet) {
1717                return colors[BACKGROUND_COLOR];
1718            }
1719            else {
1720                return SystemColor.text;
1721            }
1722        }
1723
1724        private Color getDisabledColor() {
1725            Color backgroundColor = getListBackground();
1726            Color foregroundColor = getListForeground();
1727            return (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();
1728        }
1729
1730        private boolean createBuffer() {
1731            VolatileImage localBuffer = null;
1732            XToolkit.awtLock();
1733            try {
1734                localBuffer = buffer;
1735            } finally {
1736                XToolkit.awtUnlock();
1737            }
1738
1739            if (localBuffer == null) {
1740                if (log.isLoggable(PlatformLogger.Level.FINE)) {
1741                    log.fine("Creating buffer " + width + "x" + height);
1742                }
1743                // use GraphicsConfig.cCVI() instead of Component.cVI(),
1744                // because the latter may cause a deadlock with the tree lock
1745                localBuffer =
1746                    graphicsConfig.createCompatibleVolatileImage(width+1,
1747                                                                 height+1);
1748            }
1749            XToolkit.awtLock();
1750            try {
1751                if (buffer == null) {
1752                    buffer = localBuffer;
1753                    return true;
1754                }
1755            } finally {
1756                XToolkit.awtUnlock();
1757            }
1758            return false;
1759        }
1760
1761        public void invalidate() {
1762            XToolkit.awtLock();
1763            try {
1764                if (buffer != null) {
1765                    buffer.flush();
1766                }
1767                buffer = null;
1768            } finally {
1769                XToolkit.awtUnlock();
1770            }
1771        }
1772
1773        private void paint(Graphics listG, int firstItem, int lastItem, int options) {
1774            paint(listG, firstItem, lastItem, options, null, null);
1775        }
1776
1777        private void paint(Graphics listG, int firstItem, int lastItem, int options,
1778                           Rectangle source, Point distance) {
1779            if (log.isLoggable(PlatformLogger.Level.FINER)) {
1780                log.finer("Repaint from " + firstItem + " to " + lastItem + " options " + options);
1781            }
1782            if (firstItem > lastItem) {
1783                int t = lastItem;
1784                lastItem = firstItem;
1785                firstItem = t;
1786            }
1787            if (firstItem < 0) {
1788                firstItem = 0;
1789            }
1790            colors = getGUIcolors();
1791            VolatileImage localBuffer = null;
1792            do {
1793                XToolkit.awtLock();
1794                try {
1795                    if (createBuffer()) {
1796                        // First time created buffer should be painted over at full.
1797                        options = PAINT_ALL;
1798                    }
1799                    localBuffer = buffer;
1800                } finally {
1801                    XToolkit.awtUnlock();
1802                }
1803                switch (localBuffer.validate(getGraphicsConfiguration())) {
1804                  case VolatileImage.IMAGE_INCOMPATIBLE:
1805                      invalidate();
1806                      options = PAINT_ALL;
1807                      continue;
1808                  case VolatileImage.IMAGE_RESTORED:
1809                      options = PAINT_ALL;
1810                }
1811                Graphics g = localBuffer.createGraphics();
1812
1813                // Note that the order of the following painting operations
1814                // should not be modified
1815                try {
1816                    g.setFont(getFont());
1817
1818                    // hiding the focus rectangle must be done prior to copying
1819                    // area and so this is the first action to be performed
1820                    if ((options & (PAINT_HIDEFOCUS)) != 0) {
1821                        paintFocus(g, PAINT_HIDEFOCUS);
1822                    }
1823                    /*
1824                     * The shift of the component contents occurs while someone
1825                     * scrolls the component, the only purpose of the shift is to
1826                     * increase the painting performance. The shift should be done
1827                     * prior to painting any area (except hiding focus) and actually
1828                     * it should never be done jointly with erase background.
1829                     */
1830                    if ((options & COPY_AREA) != 0) {
1831                        g.copyArea(source.x, source.y, source.width, source.height,
1832                            distance.x, distance.y);
1833                    }
1834                    if ((options & PAINT_BACKGROUND) != 0) {
1835                        paintBackground(g);
1836                        // Since we made full erase update items
1837                        firstItem = getFirstVisibleItem();
1838                        lastItem = getLastVisibleItem();
1839                    }
1840                    if ((options & PAINT_ITEMS) != 0) {
1841                        paintItems(g, firstItem, lastItem, options);
1842                    }
1843                    if ((options & PAINT_VSCROLL) != 0 && vsbVis) {
1844                        g.setClip(getVScrollBarRec());
1845                        paintVerScrollbar(g, true);
1846                    }
1847                    if ((options & PAINT_HSCROLL) != 0 && hsbVis) {
1848                        g.setClip(getHScrollBarRec());
1849                        paintHorScrollbar(g, true);
1850                    }
1851                    if ((options & (PAINT_FOCUS)) != 0) {
1852                        paintFocus(g, PAINT_FOCUS);
1853                    }
1854                } finally {
1855                    g.dispose();
1856                }
1857            } while (localBuffer.contentsLost());
1858            listG.drawImage(localBuffer, 0, 0, null);
1859        }
1860
1861        private void paintBackground(Graphics g) {
1862            g.setColor(SystemColor.window);
1863            g.fillRect(0, 0, width, height);
1864            g.setColor(getListBackground());
1865            g.fillRect(0, 0, listWidth, listHeight);
1866            draw3DRect(g, getSystemColors(), 0, 0, listWidth - 1, listHeight - 1, false);
1867        }
1868
1869        private void paintItems(Graphics g, int firstItem, int lastItem, int options) {
1870            if (log.isLoggable(PlatformLogger.Level.FINER)) {
1871                log.finer("Painting items from " + firstItem + " to " + lastItem + ", focused " + focusIndex + ", first " + getFirstVisibleItem() + ", last " + getLastVisibleItem());
1872            }
1873
1874            firstItem = Math.max(getFirstVisibleItem(), firstItem);
1875            if (firstItem > lastItem) {
1876                int t = lastItem;
1877                lastItem = firstItem;
1878                firstItem = t;
1879            }
1880            firstItem = Math.max(getFirstVisibleItem(), firstItem);
1881            lastItem = Math.min(lastItem, items.size()-1);
1882
1883            if (log.isLoggable(PlatformLogger.Level.FINER)) {
1884                log.finer("Actually painting items from " + firstItem + " to " + lastItem +
1885                          ", items in window " + itemsInWindow());
1886            }
1887            for (int i = firstItem; i <= lastItem; i++) {
1888                paintItem(g, i);
1889            }
1890        }
1891
1892        private void paintItem(Graphics g, int index) {
1893            if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1894                log.finest("Painting item " + index);
1895            }
1896            // 4895367 - only paint items which are visible
1897            if (!isItemHidden(index)) {
1898                Shape clip = g.getClip();
1899                int w = getItemWidth();
1900                int h = getItemHeight();
1901                int y = getItemY(index);
1902                int x = getItemX();
1903                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1904                    log.finest("Setting clip " + new Rectangle(x, y, w - (SPACE*2), h-(SPACE*2)));
1905                }
1906                g.setClip(x, y, w - (SPACE*2), h-(SPACE*2));
1907
1908                // Always paint the background so that focus is unpainted in
1909                // multiselect mode
1910                if (isSelected(index)) {
1911                    if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1912                        log.finest("Painted item is selected");
1913                    }
1914                    g.setColor(getListForeground());
1915                } else {
1916                    g.setColor(getListBackground());
1917                }
1918                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1919                    log.finest("Filling " + new Rectangle(x, y, w, h));
1920                }
1921                g.fillRect(x, y, w, h);
1922
1923                if (index <= getLastVisibleItem() && index < items.size()) {
1924                    if (!isEnabled()){
1925                        g.setColor(getDisabledColor());
1926                    } else if (isSelected(index)) {
1927                        g.setColor(getListBackground());
1928                    } else {
1929                        g.setColor(getListForeground());
1930                    }
1931                    String str = items.elementAt(index);
1932                    g.drawString(str, x - hsb.getValue(), y + fontAscent);
1933                } else {
1934                    // Clear the remaining area around the item - focus area and the rest of border
1935                    g.setClip(x, y, listWidth, h);
1936                    g.setColor(getListBackground());
1937                    g.fillRect(x, y, listWidth, h);
1938                }
1939                g.setClip(clip);
1940            }
1941        }
1942
1943        void paintScrollBar(XScrollbar scr, Graphics g, int x, int y, int width, int height, boolean paintAll) {
1944            if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1945                log.finest("Painting scrollbar " + scr + " width " +
1946                width + " height " + height + ", paintAll " + paintAll);
1947            }
1948            g.translate(x, y);
1949            scr.paint(g, getSystemColors(), paintAll);
1950            g.translate(-x, -y);
1951        }
1952
1953        /**
1954         * Paint the horizontal scrollbar to the screen
1955         *
1956         * @param g the graphics context to draw into
1957         * @param paintAll paint the whole scrollbar if true, just the thumb if false
1958         */
1959        void paintHorScrollbar(Graphics g, boolean paintAll) {
1960            int w = getListWidth();
1961            paintScrollBar(hsb, g, 0, height - (SCROLLBAR_WIDTH), w, SCROLLBAR_WIDTH, paintAll);
1962        }
1963
1964        /**
1965         * Paint the vertical scrollbar to the screen
1966         *
1967         * @param g the graphics context to draw into
1968         * @param paintAll paint the whole scrollbar if true, just the thumb if false
1969         */
1970        void paintVerScrollbar(Graphics g, boolean paintAll) {
1971            int h = height - (hsbVis ? (SCROLLBAR_AREA-2) : 0);
1972            paintScrollBar(vsb, g, width - SCROLLBAR_WIDTH, 0, SCROLLBAR_WIDTH - 2, h, paintAll);
1973        }
1974
1975
1976        private Rectangle prevFocusRect;
1977        private void paintFocus(Graphics g, int options) {
1978            boolean paintFocus = (options & PAINT_FOCUS) != 0;
1979            if (paintFocus && !hasFocus()) {
1980                paintFocus = false;
1981            }
1982            if (log.isLoggable(PlatformLogger.Level.FINE)) {
1983                log.fine("Painting focus, focus index " + getFocusIndex() + ", focus is " +
1984                         (isItemHidden(getFocusIndex())?("invisible"):("visible")) + ", paint focus is " + paintFocus);
1985            }
1986            Shape clip = g.getClip();
1987            g.setClip(0, 0, listWidth, listHeight);
1988            if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1989                log.finest("Setting focus clip " + new Rectangle(0, 0, listWidth, listHeight));
1990            }
1991            Rectangle rect = getFocusRect();
1992            if (prevFocusRect != null) {
1993                // Erase focus rect
1994                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1995                    log.finest("Erasing previous focus rect " + prevFocusRect);
1996                }
1997                g.setColor(getListBackground());
1998                g.drawRect(prevFocusRect.x, prevFocusRect.y, prevFocusRect.width, prevFocusRect.height);
1999                prevFocusRect = null;
2000            }
2001            if (paintFocus) {
2002                // Paint new
2003                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
2004                    log.finest("Painting focus rect " + rect);
2005                }
2006                g.setColor(getListForeground());  // Focus color is always black on Linux
2007                g.drawRect(rect.x, rect.y, rect.width, rect.height);
2008                prevFocusRect = rect;
2009            }
2010            g.setClip(clip);
2011        }
2012    }
2013}
2014