alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (XListPeer.java)

This example Java source code file (XListPeer.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, color, copy_area, dimension, event, image, itemevent, margin, none, paint_all, paint_focus, paint_items, point, rectangle, scrollbar_area, scrollbar_width, space, util

The XListPeer.java Java example source code

/*
 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


// Very much based on XListPeer from javaos

package sun.awt.X11;

import java.awt.*;
import java.awt.event.*;
import java.awt.peer.*;
import java.util.Objects;
import java.util.Vector;
import java.awt.image.*;
import sun.util.logging.PlatformLogger;

// TODO: some input actions should do nothing if Shift or Control are down

class XListPeer extends XComponentPeer implements ListPeer, XScrollbarClient {

    private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XListPeer");

    public final static int     MARGIN = 2;
    public final static int     SPACE = 1;
    public final static int     SCROLLBAR_AREA = 17;  // Area reserved for the
                                                      // scrollbar
    public final static int     SCROLLBAR_WIDTH = 13; // Actual width of the
                                                      // scrollbar
    public final static int     NONE = -1;
    public final static int     WINDOW = 0;
    public final static int     VERSCROLLBAR = 1;
    public final static int     HORSCROLLBAR = 2;
    public final static int     DEFAULT_VISIBLE_ROWS = 4; // From java.awt.List,
    public final static int     HORIZ_SCROLL_AMT = 10;

    private final static int    PAINT_VSCROLL = 2;
    private final static int    PAINT_HSCROLL = 4;
    private final static int    PAINT_ITEMS = 8;
    private final static int    PAINT_FOCUS = 16;
    private final static int    PAINT_BACKGROUND = 32;
    private final static int    PAINT_HIDEFOCUS = 64;
    private final static int    PAINT_ALL =
        PAINT_VSCROLL | PAINT_HSCROLL | PAINT_ITEMS | PAINT_FOCUS | PAINT_BACKGROUND;
    private final static int    COPY_AREA = 128;

    XVerticalScrollbar       vsb;
    XHorizontalScrollbar     hsb;
    ListPainter painter;

    // TODO: ick - Vector?
    Vector                      items;
    boolean                     multipleSelections;
    int                         active = NONE;

    // Holds the array of the indexes of the elements which is selected
    // This array should be kept sorted, low to high.
    int                         selected[];
    int                         fontHeight;
    int                         fontAscent;
    int                         fontLeading;

    // Holds the index of the item used in the previous operation (selectItem, deselectItem)
    // Adding of an item or clearing of the list sets this index to -1
    // The index is used at the moment of the post of ACTION_PERFORMED event after the mouse double click event.
    int                         currentIndex = -1;

    // Used for tracking selection/deselection between mousePress/Release
    // and for ItemEvents
    int                         eventIndex = -1;
    int                         eventType = NONE;

    // Holds the index of the item that receive focus
    // This variable is reasonable only for multiple list
    // since 'focusIndex' and 'selected[0]' are equal for single-selection list
    int                         focusIndex;

    int                         maxLength;
    boolean                     vsbVis;  // visibility of scrollbars
    boolean                     hsbVis;
    int                         listWidth;  // Width of list portion of List
    int                         listHeight; // Height of list portion of List
    // (i.e. without scrollbars)

    private int firstTimeVisibleIndex = 0;

    // Motif Lists don't seem to inherit the background color from their
    // parent when an app is first started up.  So, we track if the colors have
    // been set.  See getListBackground()/getListForeground().
    boolean bgColorSet;
    boolean fgColorSet;

    // Holds the true if mouse is dragging outside of the area of the list
    // The flag is used at the moment of the dragging and releasing mouse
    // See 6243382 for more information
    boolean mouseDraggedOutHorizontally = false;
    boolean mouseDraggedOutVertically = false;

    // Holds the true if a mouse event was originated on the scrollbar
    // See 6300527 for more information
    boolean isScrollBarOriginated = false;

    // This variable is set to true after the "mouse pressed" event and to false after the "mouse released" event
    // Fixed 6293432: Key events ('SPACE', 'UP', 'DOWN') aren't blocked if mouse is kept in 'PRESSED' state for List, XAWT
    boolean isMousePressed = false;

    /**
     * Create a list
     */
    XListPeer(List target) {
        super(target);
    }

    /**
     * Overridden from XWindow
     */
    public void preInit(XCreateWindowParams params) {
        super.preInit(params);

        // Stuff that must be initialized before layout() is called
        items = new Vector();
        createVerScrollbar();
        createHorScrollbar();

        painter = new ListPainter();

        // See 6246467 for more information
        bgColorSet = target.isBackgroundSet();
        fgColorSet = target.isForegroundSet();
    }

    public void postInit(XCreateWindowParams params) {
        super.postInit(params);
        initFontMetrics();
        // TODO: more efficient way?
        //       do we really want/need a copy of all the items?
        // get all items from target
        List l = (List)target;
        int stop = l.getItemCount();
        for (int i = 0 ; i < stop; i++) {
            items.addElement(l.getItem(i));
        }

        /* make the visible position visible. */
        int index = l.getVisibleIndex();
        if (index >= 0) {
            // Can't call makeVisible since it check scroll bar,
            // initialize scroll bar instead
            vsb.setValues(index, 0, 0, items.size());
        }

        // NOTE: needs to have target set
        maxLength = maxLength();

        // get the index containing all indexes to selected items
        int sel[] = l.getSelectedIndexes();
        selected = new int[sel.length];
        // TODO: shouldn't this be arraycopy()?
        for (int i = 0 ; i < sel.length ; i ++) {
            selected[i] = sel[i];
        }
        // The select()ed item should become the focused item, but we don't
        // get the select() call because the peer generally hasn't yet been
        // created during app initialization.
        // TODO: For multi-select lists, it should be the highest selected index
        if (sel.length > 0) {
            setFocusIndex(sel[sel.length - 1]);
        }
        else {
            setFocusIndex(0);
        }

        multipleSelections = l.isMultipleMode();
    }


    /**
     * add Vertical Scrollbar
     */
    void createVerScrollbar() {
        vsb = new XVerticalScrollbar(this);
        vsb.setValues(0, 0, 0, 0, 1, 1);
    }


    /**
     * add Horizontal scrollbar
     */
    void createHorScrollbar() {
        hsb = new XHorizontalScrollbar(this);
        hsb.setValues(0, 0, 0, 0, HORIZ_SCROLL_AMT, HORIZ_SCROLL_AMT);
    }

    /* New method name for 1.1 */
    public void add(String item, int index) {
        addItem(item, index);
    }

    /* New method name for 1.1 */
    public void removeAll() {
        clear();
        maxLength = 0;
    }

    /* New method name for 1.1 */
    public void setMultipleMode (boolean b) {
        setMultipleSelections(b);
    }

    /* New method name for 1.1 */
    public Dimension getPreferredSize(int rows) {
        return preferredSize(rows);
    }

    /* New method name for 1.1 */
    public Dimension getMinimumSize(int rows) {
        return minimumSize(rows);
    }

    /**
     * Minimum size.
     */
    public Dimension minimumSize() {
        return minimumSize(DEFAULT_VISIBLE_ROWS);
    }

    /**
     * return the preferredSize
     */
    public Dimension preferredSize(int v) {
        return minimumSize(v);
    }

    /**
     * return the minimumsize
     */
    public Dimension minimumSize(int v) {
        FontMetrics fm = getFontMetrics(getFont());
        initFontMetrics();
        return new Dimension(20 + fm.stringWidth("0123456789abcde"),
                             getItemHeight() * v + (2*MARGIN));
    }

    /**
     * Calculate font metrics
     */
    void initFontMetrics() {
        FontMetrics fm = getFontMetrics(getFont());
        fontHeight = fm.getHeight();
        fontAscent = fm.getAscent();
        fontLeading = fm.getLeading();
    }


    /**
     * return the length of the largest item in the list
     */
    int maxLength() {
        FontMetrics fm = getFontMetrics(getFont());
        int m = 0;
        int end = items.size();
        for(int i = 0 ; i < end ; i++) {
            int l = fm.stringWidth(((String)items.elementAt(i)));
            m = Math.max(m, l);
        }
        return m;
    }

    /**
     * Calculates the width of item's label
     */
    int getItemWidth(int i) {
        FontMetrics fm = getFontMetrics(getFont());
        return fm.stringWidth((String)items.elementAt(i));
    }

    /**
     * return the on-screen width of the given string "str"
     */
    int stringLength(String str) {
        FontMetrics fm = getFontMetrics(target.getFont());
        return fm.stringWidth(str);
    }

    public void setForeground(Color c) {
        fgColorSet = true;
        super.setForeground(c);
    }

    public void setBackground(Color c) {
        bgColorSet = true;
        super.setBackground(c);
    }

    /**
     * Returns the color that should be used to paint the background of
     * the list of items.  Note that this is not the same as
     * target.getBackground() which is the color of the scrollbars, and the
     * lower-right corner of the Component when the scrollbars are displayed.
     */
    private Color getListBackground(Color[] colors) {
        if (bgColorSet) {
            return colors[BACKGROUND_COLOR];
        }
        else {
            return SystemColor.text;
        }
    }

    /**
     * Returns the color that should be used to paint the list item text.
     */
    private Color getListForeground(Color[] colors) {
        if (fgColorSet) {
            return colors[FOREGROUND_COLOR];
        }
        else {
            return SystemColor.textText;
        }
    }

    Rectangle getVScrollBarRec() {
        return new Rectangle(width - (SCROLLBAR_WIDTH), 0, SCROLLBAR_WIDTH+1, height);
    }

    Rectangle getHScrollBarRec() {
        return new Rectangle(0, height - SCROLLBAR_WIDTH, width, SCROLLBAR_WIDTH);
    }

    int getFirstVisibleItem() {
        if (vsbVis) {
            return vsb.getValue();
        } else {
            return 0;
        }
    }

    int getLastVisibleItem() {
        if (vsbVis) {
            return Math.min(items.size()-1, vsb.getValue() + itemsInWindow() -1);
        } else {
            return Math.min(items.size()-1, itemsInWindow()-1);
        }
    }
    public void repaintScrollbarRequest(XScrollbar scrollbar) {
        if (scrollbar == hsb)  {
            repaint(PAINT_HSCROLL);
        }
        else if (scrollbar == vsb) {
            repaint(PAINT_VSCROLL);
        }
    }
    /**
     * Overridden for performance
     */
    public void repaint() {
        repaint(getFirstVisibleItem(), getLastVisibleItem(), PAINT_ALL);
    }

    private void repaint(int options) {
        repaint(getFirstVisibleItem(), getLastVisibleItem(), options);
    }

    private void repaint(int firstItem, int lastItem, int options) {
        repaint(firstItem, lastItem, options, null, null);
    }

    /**
     * In most cases the entire area of the component doesn't have
     * to be repainted. The method repaints the particular areas of
     * the component. The areas to repaint is specified by the option
     * parameter. The possible values of the option parameter are:
     * PAINT_VSCROLL, PAINT_HSCROLL, PAINT_ITEMS, PAINT_FOCUS,
     * PAINT_HIDEFOCUS, PAINT_BACKGROUND, PAINT_ALL, COPY_AREA.
     *
     * Note that the COPY_AREA value initiates copy of a source area
     * of the component by a distance by means of the copyArea method
     * of the Graphics class.
     *
     * @param firstItem the position of the first item of the range to repaint
     * @param lastItem the position of the last item of the range to repaint
     * @param options specifies the particular area of the component to repaint
     * @param source the area of the component to copy
     * @param distance the distance to copy the source area
     */
    private void repaint(int firstItem, int lastItem, int options, Rectangle source, Point distance) {
        final Graphics g = getGraphics();
        if (g != null) {
            try {
                painter.paint(g, firstItem, lastItem, options, source, distance);
                postPaintEvent(target, 0, 0, getWidth(), getHeight());
            } finally {
                g.dispose();
            }
        }
    }
    @Override
    void paintPeer(final Graphics g) {
        painter.paint(g, getFirstVisibleItem(), getLastVisibleItem(), PAINT_ALL);
    }
    public boolean isFocusable() { return true; }

    // TODO: share/promote the Focus methods?
    public void focusGained(FocusEvent e) {
        super.focusGained(e);
        repaint(PAINT_FOCUS);
    }

    public void focusLost(FocusEvent e) {
        super.focusLost(e);
        repaint(PAINT_FOCUS);
    }

    /**
     * Layout the sub-components of the List - that is, the scrollbars and the
     * list of items.
     */
    public void layout() {
        int vis, maximum;
        boolean vsbWasVisible;
        int origVSBVal;
        assert(target != null);

        // Start with assumption there is not a horizontal scrollbar,
        // see if we need a vertical scrollbar

        // Bug: If the list DOES have a horiz scrollbar and the value is set to
        // the very bottom value, value is reset in setValues() because it isn't
        // a valid value for cases when the list DOESN'T have a horiz scrollbar.
        // This is currently worked-around with origVSGVal.
        origVSBVal = vsb.getValue();
        vis = itemsInWindow(false);
        maximum = items.size() < vis ? vis : items.size();
        vsb.setValues(vsb.getValue(), vis, vsb.getMinimum(), maximum);
        vsbVis = vsbWasVisible = vsbIsVisible(false);
        listHeight = height;

        // now see if we need a horizontal scrollbar
        listWidth = getListWidth();
        vis = listWidth - ((2 * SPACE) + (2 * MARGIN));
        maximum = maxLength < vis ? vis : maxLength;
        hsb.setValues(hsb.getValue(), vis, hsb.getMinimum(), maximum);
        hsbVis = hsbIsVisible(vsbVis);

        if (hsbVis) {
            // do need a horizontal scrollbar, so recalculate height of
            // vertical s crollbar
            listHeight = height - SCROLLBAR_AREA;
            vis = itemsInWindow(true);
            maximum = items.size() < vis ? vis : items.size();
            vsb.setValues(origVSBVal, vis, vsb.getMinimum(), maximum);
            vsbVis = vsbIsVisible(true);
        }

        // now check to make sure we haven't changed need for vertical
        // scrollbar - if we have, we need to
        // recalculate horizontal scrollbar width - then we're done...
        if (vsbWasVisible != vsbVis) {
            listWidth = getListWidth();
            vis = listWidth - ((2 * SPACE) + (2 * MARGIN));
            maximum = maxLength < vis ? 0 : maxLength;
            hsb.setValues(hsb.getValue(), vis, hsb.getMinimum(), maximum);
            hsbVis = hsbIsVisible(vsbVis);
        }

        vsb.setSize(SCROLLBAR_WIDTH, listHeight);
        hsb.setSize(listWidth, SCROLLBAR_WIDTH);

        vsb.setBlockIncrement(itemsInWindow());
        hsb.setBlockIncrement(width - ((2 * SPACE) + (2 * MARGIN) + (vsbVis ? SCROLLBAR_AREA : 0)));
    }

    int getItemWidth() {
        return width - ((2 * MARGIN) + (vsbVis ? SCROLLBAR_AREA : 0));
    }

    /* Returns height of an item in the list */
    int getItemHeight() {
        return (fontHeight - fontLeading) + (2*SPACE);
    }

    int getItemX() {
        return MARGIN + SPACE;
    }

    int getItemY(int item) {
        return index2y(item);
    }

    int getFocusIndex() {
        return focusIndex;
    }

    void setFocusIndex(int value) {
        focusIndex = value;
    }

    /**
     * Update and return the focus rectangle.
     * Focus is around the focused item, if it is visible, or
     * around the border of the list if the focused item is scrolled off the top
     * or bottom of the list.
     */
    Rectangle getFocusRect() {
        Rectangle focusRect = new Rectangle();
        // width is always only based on presence of vert sb
        focusRect.x = 1;
        focusRect.width = getListWidth() - 3;
        // if focused item is not currently displayed in the list,  paint
        // focus around entire list (not including scrollbars)
        if (isIndexDisplayed(getFocusIndex())) {
            // focus rect is around the item
            focusRect.y = index2y(getFocusIndex()) - 2;
            focusRect.height = getItemHeight()+1;
        } else {
            // focus rect is around the list
            focusRect.y = 1;
            focusRect.height = hsbVis ? height - SCROLLBAR_AREA : height;
            focusRect.height -= 3;
        }
        return focusRect;
    }

    public void handleConfigureNotifyEvent(XEvent xev) {
        super.handleConfigureNotifyEvent(xev);

        // Update buffer
        painter.invalidate();
    }
    public boolean handlesWheelScrolling() { return true; }

    // FIXME: need to support MouseWheel scrolling, too
    void handleJavaMouseEvent(MouseEvent e) {
        super.handleJavaMouseEvent(e);
        int i = e.getID();
        switch (i) {
          case MouseEvent.MOUSE_PRESSED:
              mousePressed(e);
              break;
          case MouseEvent.MOUSE_RELEASED:
              mouseReleased(e);
              break;
          case MouseEvent.MOUSE_DRAGGED:
              mouseDragged(e);
              break;
        }
    }

    void handleJavaMouseWheelEvent(MouseWheelEvent e) {
        if (ListHelper.doWheelScroll(vsbVis ? vsb : null,
                                     hsbVis ? hsb : null, e)) {
            repaint();
        }
    }

    void mousePressed(MouseEvent mouseEvent) {
        if (log.isLoggable(PlatformLogger.Level.FINER)) {
            log.finer(mouseEvent.toString() + ", hsb " + hsbVis + ", vsb " + vsbVis);
        }
        if (isEnabled() && mouseEvent.getButton() == MouseEvent.BUTTON1) {
            if (inWindow(mouseEvent.getX(), mouseEvent.getY())) {
                if (log.isLoggable(PlatformLogger.Level.FINE)) {
                    log.fine("Mouse press in items area");
                }
                active = WINDOW;
                int i = y2index(mouseEvent.getY());
                if (i >= 0) {
                    if (multipleSelections) {
                        if (isSelected(i)) {
                            // See 6243382 for more information
                            deselectItem(i);
                            eventIndex = i;
                            eventType = ItemEvent.DESELECTED;
                        }
                        else {
                            selectItem(i);
                            eventIndex = i;
                            eventType = ItemEvent.SELECTED;
                        }
                    }
                    // Backward-compatible bug: even if a single-select
                    // item is already selected, we send an ITEM_STATE_CHANGED/
                    // SELECTED event.  Engineer's Toolbox appears to rely on
                    // this.
                    //else if (!isSelected(i)) {
                    else {
                        selectItem(i);
                        eventIndex = i;
                        eventType = ItemEvent.SELECTED;
                    }
                    // Restoring Windows behaviour
                    // We should update focus index after "mouse pressed" event
                    setFocusIndex(i);
                    repaint(PAINT_FOCUS);
                } else {
                    // 6426186: reset variable to prevent action event
                    // if user clicks on unoccupied area of list
                    currentIndex = -1;
                }
            } else if (inVerticalScrollbar(mouseEvent.getX(), mouseEvent.getY())) {
                if (log.isLoggable(PlatformLogger.Level.FINE)) {
                    log.fine("Mouse press in vertical scrollbar");
                }
                active = VERSCROLLBAR;
                vsb.handleMouseEvent(mouseEvent.getID(),
                                     mouseEvent.getModifiers(),
                                     mouseEvent.getX() - (width - SCROLLBAR_WIDTH),
                                     mouseEvent.getY());
            } else if (inHorizontalScrollbar(mouseEvent.getX(), mouseEvent.getY())) {
                if (log.isLoggable(PlatformLogger.Level.FINE)) {
                    log.fine("Mouse press in horizontal scrollbar");
                }
                active = HORSCROLLBAR;
                hsb.handleMouseEvent(mouseEvent.getID(),
                                     mouseEvent.getModifiers(),
                                     mouseEvent.getX(),
                                     mouseEvent.getY() - (height - SCROLLBAR_WIDTH));

            }
            isMousePressed = true;
        }
    }
    void mouseReleased(MouseEvent mouseEvent) {
        if (isEnabled() && mouseEvent.getButton() == MouseEvent.BUTTON1) {
            //winReleaseCursorFocus();
            int clickCount = mouseEvent.getClickCount();
            if (active == VERSCROLLBAR) {
                vsb.handleMouseEvent(mouseEvent.getID(),
                                     mouseEvent.getModifiers(),
                                     mouseEvent.getX()-(width-SCROLLBAR_WIDTH),
                                     mouseEvent.getY());
            } else if(active == HORSCROLLBAR) {
                hsb.handleMouseEvent(mouseEvent.getID(),
                                     mouseEvent.getModifiers(),
                                     mouseEvent.getX(),
                                     mouseEvent.getY()-(height-SCROLLBAR_WIDTH));
            } else if ( ( currentIndex >= 0 ) && ( clickCount >= 2 ) &&
                        ( clickCount % 2 == 0 ) ) {
                postEvent(new ActionEvent(target,
                                          ActionEvent.ACTION_PERFORMED,
                                          (String)items.elementAt(currentIndex),
                                          mouseEvent.getWhen(),
                                          mouseEvent.getModifiers()));  // No ext mods
            } else if (active == WINDOW) {
                // See 6243382 for more information
                trackMouseReleasedScroll();

                if (eventType == ItemEvent.DESELECTED) {
                    assert multipleSelections : "Shouldn't get a deselect for a single-select List";
                    // Paint deselection the release
                    deselectItem(eventIndex);
                }
                if (eventType != NONE) {
                    postEvent(new ItemEvent((List)target,
                                ItemEvent.ITEM_STATE_CHANGED,
                                Integer.valueOf(eventIndex),
                                eventType));
                }
            }
            active = NONE;
            eventIndex = -1;
            eventType = NONE;
            isMousePressed = false;
        }
    }

    void mouseDragged(MouseEvent mouseEvent) {
        // TODO: can you drag w/ any other buttons?  what about multiple buttons?
        if (isEnabled() &&
            (mouseEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) {
            if ((active == VERSCROLLBAR)) {
                vsb.handleMouseEvent(mouseEvent.getID(),
                                     mouseEvent.getModifiers(),
                                     mouseEvent.getX()-(width-SCROLLBAR_WIDTH),
                                     mouseEvent.getY());
            } else if ((active == HORSCROLLBAR)) {
                hsb.handleMouseEvent(mouseEvent.getID(),
                                     mouseEvent.getModifiers(),
                                     mouseEvent.getX(),
                                     mouseEvent.getY()-(height-SCROLLBAR_WIDTH));
            } else if (active == WINDOW) {
                int i = y2index(mouseEvent.getY());
                if (multipleSelections) {
                    // Multi-select only:
                    // If a selected item was pressed on and then dragged off
                    // of, cancel the pending deselect.
                    if (eventType == ItemEvent.DESELECTED) {
                        if (i != eventIndex) {
                            eventType = NONE;
                            eventIndex = -1;
                        }
                    }
                }
                else if (eventType == ItemEvent.SELECTED) {
                    // Single-select only:
                    // If an unselected item was pressed on, track the drag
                    // and select the item under the mouse

                    // See 6243382 for more information
                    trackMouseDraggedScroll(mouseEvent);

                    if (i >= 0 && !isSelected(i)) {
                        int oldSel = eventIndex;
                        selectItem(i);
                        eventIndex = i;
                        repaint(oldSel, eventIndex, PAINT_ITEMS);
                    }
                }
                // Restoring Windows behaviour
                // We should update focus index after "mouse dragged" event
                if (i >= 0) {
                    setFocusIndex(i);
                    repaint(PAINT_FOCUS);
                }
            }
        }
    }

    /*
     * Helper method for XListPeer with integrated vertical scrollbar.
     * Start or stop vertical scrolling when mouse dragged in / out the area of the list if it's required
     * Restoring Motif behavior
     * See 6243382 for more information
     */
    void trackMouseDraggedScroll(MouseEvent mouseEvent){

        if (vsb.beforeThumb(mouseEvent.getX(), mouseEvent.getY())) {
            vsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
        } else {
            vsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
        }

        if(mouseEvent.getY() < 0 || mouseEvent.getY() >= listHeight){
            if (!mouseDraggedOutVertically){
                mouseDraggedOutVertically = true;
                vsb.startScrollingInstance();
            }
        }else{
            if (mouseDraggedOutVertically){
                mouseDraggedOutVertically = false;
                vsb.stopScrollingInstance();
            }
        }

        if (hsb.beforeThumb(mouseEvent.getX(), mouseEvent.getY())) {
            hsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
        } else {
            hsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
        }

        if (mouseEvent.getX() < 0 || mouseEvent.getX() >= listWidth) {
            if (!mouseDraggedOutHorizontally){
                mouseDraggedOutHorizontally = true;
                hsb.startScrollingInstance();
            }
        }else{
            if (mouseDraggedOutHorizontally){
                mouseDraggedOutHorizontally = false;
                hsb.stopScrollingInstance();
            }
        }
    }

    /*
     * Helper method for XListPeer with integrated vertical scrollbar.
     * Stop vertical scrolling when mouse released in / out the area of the list if it's required
     * Restoring Motif behavior
     * see 6243382 for more information
     */
    void trackMouseReleasedScroll(){

        if (mouseDraggedOutVertically){
            mouseDraggedOutVertically = false;
            vsb.stopScrollingInstance();
        }

        if (mouseDraggedOutHorizontally){
            mouseDraggedOutHorizontally = false;
            hsb.stopScrollingInstance();
        }
    }

    void handleJavaKeyEvent(KeyEvent e) {
        switch(e.getID()) {
          case KeyEvent.KEY_PRESSED:
              if (!isMousePressed){
                  keyPressed(e);
              }
              break;
        }
    }

    void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (log.isLoggable(PlatformLogger.Level.FINE)) {
            log.fine(e.toString());
        }
        switch(keyCode) {
          case KeyEvent.VK_UP:
          case KeyEvent.VK_KP_UP: // TODO: I assume we also want this, too
              if (getFocusIndex() > 0) {
                  setFocusIndex(getFocusIndex()-1);
                  repaint(PAINT_HIDEFOCUS);
                  // If single-select, select the item
                  if (!multipleSelections) {
                      selectItem(getFocusIndex());
                      postEvent(new ItemEvent((List)target,
                                              ItemEvent.ITEM_STATE_CHANGED,
                                              Integer.valueOf(getFocusIndex()),
                                              ItemEvent.SELECTED));
                  }
                  if (isItemHidden(getFocusIndex())) {
                      makeVisible(getFocusIndex());
                  }
                  else {
                      repaint(PAINT_FOCUS);
                  }
              }
              break;
          case KeyEvent.VK_DOWN:
          case KeyEvent.VK_KP_DOWN: // TODO: I assume we also want this, too
              if (getFocusIndex() < items.size() - 1) {
                  setFocusIndex(getFocusIndex()+1);
                  repaint(PAINT_HIDEFOCUS);
                  // If single-select, select the item
                  if (!multipleSelections) {
                      selectItem(getFocusIndex());
                      postEvent(new ItemEvent((List)target,
                                              ItemEvent.ITEM_STATE_CHANGED,
                                              Integer.valueOf(getFocusIndex()),
                                              ItemEvent.SELECTED));
                  }
                  if (isItemHidden(getFocusIndex())) {
                      makeVisible(getFocusIndex());
                  }
                  else {
                      repaint(PAINT_FOCUS);
                  }
              }
              break;
          case KeyEvent.VK_PAGE_UP: {
              // Assumes that scrollbar does its own bounds-checking
              int previousValue = vsb.getValue();
              vsb.setValue(vsb.getValue() - vsb.getBlockIncrement());
              int currentValue = vsb.getValue();
              // 6190768 pressing pg-up on AWT multiple selection lists the items but no item event is triggered, on XToolkit
              // Restoring Motif behavior
              if (previousValue!=currentValue) {
                  setFocusIndex(Math.max(getFocusIndex()-itemsInWindow(), 0));
                  if (!multipleSelections){
                      selectItem(getFocusIndex());
                      postEvent(new ItemEvent((List)target,
                                              ItemEvent.ITEM_STATE_CHANGED,
                                              Integer.valueOf(getFocusIndex()),
                                              ItemEvent.SELECTED));
                  }
              }
              repaint();
              break;
          }
          case KeyEvent.VK_PAGE_DOWN: {
              // Assumes that scrollbar does its own bounds-checking
              int previousValue = vsb.getValue();
              vsb.setValue(vsb.getValue() + vsb.getBlockIncrement());
              int currentValue = vsb.getValue();
              // 6190768 pressing pg-down on AWT multiple selection list selects the items but no item event is triggered, on XToolkit
              // Restoring Motif behavior
              if (previousValue!=currentValue) {
                  setFocusIndex(Math.min(getFocusIndex() + itemsInWindow(), items.size()-1));
                  if (!multipleSelections){
                      selectItem(getFocusIndex());
                      postEvent(new ItemEvent((List)target,
                                              ItemEvent.ITEM_STATE_CHANGED,
                                              Integer.valueOf(getFocusIndex()),
                                              ItemEvent.SELECTED));
                  }
              }
              repaint();
              break;
          }
          case KeyEvent.VK_LEFT:
          case KeyEvent.VK_KP_LEFT:
              if (hsbVis & hsb.getValue() > 0) {
                  hsb.setValue(hsb.getValue() - HORIZ_SCROLL_AMT);
                  repaint();
              }
              break;
          case KeyEvent.VK_RIGHT:
          case KeyEvent.VK_KP_RIGHT:
              if (hsbVis) { // Should check if already at end
                  hsb.setValue(hsb.getValue() + HORIZ_SCROLL_AMT);
                  repaint();
              }
              break;
          // 6190778 CTRL + HOME, CTRL + END keys do not work properly for list on XToolkit
          // Restoring Motif behavior
          case KeyEvent.VK_HOME:
              if (!e.isControlDown() || ((List)target).getItemCount() <= 0)
                  break;
              if (vsbVis) {
                  vsb.setValue(vsb.getMinimum());
              }
              setFocusIndex(0);
              if (!multipleSelections) {
                  selectItem(getFocusIndex());
                  postEvent(new ItemEvent((List)target,
                                          ItemEvent.ITEM_STATE_CHANGED,
                                          Integer.valueOf(getFocusIndex()),
                                          ItemEvent.SELECTED));
              }
              repaint();
              break;
          case KeyEvent.VK_END:
              if (!e.isControlDown() || ((List)target).getItemCount() <= 0)
                  break;
              if (vsbVis) {
                  vsb.setValue(vsb.getMaximum());
              }
              setFocusIndex(items.size()-1);
              if (!multipleSelections) {
                  selectItem(getFocusIndex());
                  postEvent(new ItemEvent((List)target,
                                          ItemEvent.ITEM_STATE_CHANGED,
                                          Integer.valueOf(getFocusIndex()),
                                          ItemEvent.SELECTED));
              }
              repaint();
              break;
          case KeyEvent.VK_SPACE:
              // Fixed 6299853: XToolkit: Pressing space triggers ItemStateChanged event after List.removeAll called
              // If getFocusIndex() is less than 0, the event will not be triggered when space pressed
              if (getFocusIndex() < 0 || ((List)target).getItemCount() <= 0) {
                  break;
              }

              boolean isSelected = isSelected(getFocusIndex());

              // Spacebar only deselects for multi-select Lists
              if (multipleSelections && isSelected) {
                  deselectItem(getFocusIndex());
                  postEvent(new ItemEvent((List)target,
                                          ItemEvent.ITEM_STATE_CHANGED,
                                          Integer.valueOf(getFocusIndex()),
                                          ItemEvent.DESELECTED));
              }
              else if (!isSelected) { // Note: this changes the Solaris/Linux
                  // behavior to match that of win32.
                  // That is, pressing space bar on a
                  // single-select list when the focused
                  // item is already selected does NOT
                  // send an ItemEvent.SELECTED event.
                  selectItem(getFocusIndex());
                  postEvent(new ItemEvent((List)target,
                                          ItemEvent.ITEM_STATE_CHANGED,
                                          Integer.valueOf(getFocusIndex()),
                                          ItemEvent.SELECTED));
              }
              break;
          case KeyEvent.VK_ENTER:
              // It looks to me like there are bugs as well as inconsistencies
              // in the way the Enter key is handled by both Solaris and Windows.
              // So for now in XAWT, I'm going to simply go by what the List docs
              // say: "AWT also generates an action event when the user presses
              // the return key while an item in the list is selected."
              if (selected.length > 0) {
                  postEvent(new ActionEvent((List)target,
                                            ActionEvent.ACTION_PERFORMED,
                                            (String)items.elementAt(getFocusIndex()),
                                            e.getWhen(),
                                            e.getModifiers()));  // ActionEvent doesn't have
                  // extended modifiers.
              }
              break;
        }
    }

    /**
     * return value from the scrollbar
     */
    public void notifyValue(XScrollbar obj, int type, int v, boolean isAdjusting) {

        if (log.isLoggable(PlatformLogger.Level.FINE)) {
            log.fine("Notify value changed on " + obj + " to " + v);
        }
        int value = obj.getValue();
        if (obj == vsb) {
            scrollVertical(v - value);

            // See 6243382 for more information
            int oldSel = eventIndex;
            int newSel = eventIndex+v-value;
            if (mouseDraggedOutVertically && !isSelected(newSel)){
                selectItem(newSel);
                eventIndex = newSel;
                repaint(oldSel, eventIndex, PAINT_ITEMS);
                // Scrolling select() should also set the focus index
                // Otherwise, the updating of the 'focusIndex' variable will be incorrect
                // if user drag mouse out of the area of the list
                setFocusIndex(newSel);
                repaint(PAINT_FOCUS);
            }

        } else if ((XHorizontalScrollbar)obj == hsb) {
            scrollHorizontal(v - value);
        }

    }

    /**
     * deselect all items in List
     */
    private void deselectAllItems() {
        selected = new int [0];
        repaint(PAINT_ITEMS);
    }

    /**
     * set multiple selections
     */
    public void setMultipleSelections(boolean v) {
        if (multipleSelections != v) {
            if ( !v) {
                int selPos = ( isSelected( focusIndex )) ? focusIndex: -1;
                deselectAllItems();
                if (selPos != -1){
                    selectItem(selPos);
                }
            }
            multipleSelections = v;
        }
    }

    /**
     * add an item
     * if the index of the item is < 0 or >= than items.size()
     * then add the item to the end of the list
     */
    public void addItem(String item, int i) {
        int oldMaxLength = maxLength;
        boolean hsbWasVis = hsbVis;
        boolean vsbWasVis = vsbVis;

        int addedIndex = 0; // Index where the new item ended up
        if (i < 0 || i >= items.size()) {
            i = -1;
        }

        // Why we set this variable to -1 in spite of the fact that selected[] is changed in other way?
        // It's not clear how to reproduce incorrect behaviour based on this assignment
        // since before using this variable (mouseReleased) we certainly update it to correct value
        // So we don't modify this behaviour now
        currentIndex = -1;

        if (i == -1) {
            items.addElement(item);
            i = 0;              // fix the math for the paintItems test
            addedIndex = items.size() - 1;
        } else {
            items.insertElementAt(item, i);
            addedIndex = i;
            for (int j = 0 ; j < selected.length ; j++) {
                if (selected[j] >= i) {
                    selected[j] += 1;
                }
            }
        }
        if (log.isLoggable(PlatformLogger.Level.FINER)) {
            log.finer("Adding item '" + item + "' to " + addedIndex);
        }

        // Update maxLength
        boolean repaintItems = !isItemHidden(addedIndex);
        maxLength = Math.max(maxLength, getItemWidth(addedIndex));
        layout();

        int options = 0;
        if (vsbVis != vsbWasVis || hsbVis != hsbWasVis) {
            // Scrollbars are being added or removed, so we must repaint all
            options = PAINT_ALL;
        }
        else {
            options = (repaintItems ? (PAINT_ITEMS):0)
                | ((maxLength != oldMaxLength || (hsbWasVis ^ hsbVis))?(PAINT_HSCROLL):0)
                | ((vsb.needsRepaint())?(PAINT_VSCROLL):0);

        }
        if (log.isLoggable(PlatformLogger.Level.FINEST)) {
            log.finest("Last visible: " + getLastVisibleItem() +
            ", hsb changed : " + (hsbWasVis ^ hsbVis) + ", items changed " + repaintItems);
        }
        repaint(addedIndex, getLastVisibleItem(), options);
    }

    /**
     * delete items starting with s (start position) to e (end position) including s and e
     * if s < 0 then s = 0
     * if e >= items.size() then e = items.size() - 1
     */
    public void delItems(int s, int e) {
        // save the current state of the scrollbars
        boolean hsbWasVisible = hsbVis;
        boolean vsbWasVisible = vsbVis;
        int oldLastDisplayed = lastItemDisplayed();

        if (log.isLoggable(PlatformLogger.Level.FINE)) {
            log.fine("Deleting from " + s + " to " + e);
        }

        if (log.isLoggable(PlatformLogger.Level.FINEST)) {
            log.finest("Last displayed item: " + oldLastDisplayed + ", items in window " + itemsInWindow() +
            ", size " + items.size());
        }

        if (items.size() == 0) {
            return;
        }

        // if user passed in flipped args, reverse them
        if (s > e) {
            int tmp = s;
            s = e;
            e = tmp;
        }

        // check for starting point less than zero
        if (s < 0) {
            s = 0;
        }

        // check for end point greater than the size of the list
        if (e >= items.size()) {
            e = items.size() - 1;
        }

        // determine whether we're going to delete any visible elements
        // repaint must also be done if scrollbars appear/disappear, which
        // can happen from removing a non-showing list item
        /*
          boolean repaintNeeded =
          ((s <= lastItemDisplayed()) && (e >= vsb.getValue()));
        */
        boolean repaintNeeded = (s >= getFirstVisibleItem() && s <= getLastVisibleItem());

        // delete the items out of the items list and out of the selected list
        for (int i = s ; i <= e ; i++) {
            items.removeElementAt(s);
            int j = posInSel(i);
            if (j != -1) {
                int newsel[] = new int[selected.length - 1];
                System.arraycopy(selected, 0, newsel, 0, j);
                System.arraycopy(selected, j + 1, newsel, j, selected.length - (j + 1));
                selected = newsel;
            }

        }

        // update the indexes in the selected array
        int diff = (e - s) + 1;
        for (int i = 0 ; i < selected.length ; i++) {
            if (selected[i] > e) {
                selected[i] -= diff;
            }
        }

        int options = PAINT_VSCROLL;
        // focusedIndex updating according to native (Window, Motif) behaviour
        if (getFocusIndex() > e) {
            setFocusIndex(getFocusIndex() - (e - s + 1));
            options |= PAINT_FOCUS;
        } else if (getFocusIndex() >= s && getFocusIndex() <= e) {
            // Fixed 6299858: PIT. Focused border not shown on List if selected item is removed, XToolkit
            // We should set focus to new first item if the current first item was removed
            // except if the list is empty
            int focusBound = (items.size() > 0) ? 0 : -1;
            setFocusIndex(Math.max(s-1, focusBound));
            options |= PAINT_FOCUS;
        }

        if (log.isLoggable(PlatformLogger.Level.FINEST)) {
            log.finest("Multiple selections: " + multipleSelections);
        }

        // update vsb.val
        if (vsb.getValue() >= s) {
            if (vsb.getValue() <= e) {
                vsb.setValue(e+1 - diff);
            } else {
                vsb.setValue(vsb.getValue() - diff);
            }
        }

        int oldMaxLength = maxLength;
        maxLength = maxLength();
        if (maxLength != oldMaxLength) {
            // Width of the items changed affecting the range of
            // horizontal scrollbar
            options |= PAINT_HSCROLL;
        }
        layout();
        repaintNeeded |= (vsbWasVisible ^ vsbVis) || (hsbWasVisible ^ hsbVis); // If scrollbars visibility changed
        if (repaintNeeded) {
            options |= PAINT_ALL;
        }
        repaint(s, oldLastDisplayed, options);
    }

    /**
     * ListPeer method
     */
    public void select(int index) {
        // Programmatic select() should also set the focus index
        setFocusIndex(index);
        repaint(PAINT_FOCUS);
        selectItem(index);
    }

    /**
     * select the index
     * redraw the list to the screen
     */
    void selectItem(int index) {
        // NOTE: instead of recalculating and the calling repaint(), painting
        // is done immediately

        // 6190746 List does not trigger ActionEvent when double clicking a programmatically selected item, XToolkit
        // If we invoke select(int) before setVisible(boolean), then variable currentIndex will equals -1. At the same time isSelected may be true.
        // Restoring Motif behavior
        currentIndex = index;

        if (isSelected(index)) {
            return;
        }
        if (!multipleSelections) {
            if (selected.length == 0) { // No current selection
                selected = new int[1];
                selected[0] = index;
            }
            else {
                int oldSel = selected[0];
                selected[0] = index;
                if (!isItemHidden(oldSel)) {
                    // Only bother painting if item is visible (4895367)
                    repaint(oldSel, oldSel, PAINT_ITEMS);
                }
            }
        } else {
            // insert "index" into the selection array
            int newsel[] = new int[selected.length + 1];
            int i = 0;
            while (i < selected.length && index > selected[i]) {
                newsel[i] = selected[i];
                i++;
            }
            newsel[i] = index;
            System.arraycopy(selected, i, newsel, i+1, selected.length - i);
            selected = newsel;
        }
        if (!isItemHidden(index)) {
            // Only bother painting if item is visible (4895367)
            repaint(index, index, PAINT_ITEMS);
        }
    }

    /**
     * ListPeer method
     * focusedIndex isn't updated according to native (Window, Motif) behaviour
     */
    public void deselect(int index) {
        deselectItem(index);
    }

    /**
     * deselect the index
     * redraw the list to the screen
     */
    void deselectItem(int index) {
        if (!isSelected(index)) {
            return;
        }
        if (!multipleSelections) {
            // TODO: keep an int[0] and int[1] around and just use them instead
            // creating new ones all the time
            selected = new int[0];
        } else {
            int i = posInSel(index);
            int newsel[] = new int[selected.length - 1];
            System.arraycopy(selected, 0, newsel, 0, i);
            System.arraycopy(selected, i+1, newsel, i, selected.length - (i+1));
            selected = newsel;
        }
        currentIndex = index;
        if (!isItemHidden(index)) {
            // Only bother repainting if item is visible
            repaint(index, index, PAINT_ITEMS);
        }
    }

    /**
     * ensure that the given index is visible, scrolling the List
     * if necessary, or doing nothing if the item is already visible.
     * The List must be repainted for changes to be visible.
     */
    public void makeVisible(int index) {
        if (index < 0 || index >= items.size()) {
            return;
        }
        if (isItemHidden(index)) {  // Do I really need to call this?
            // If index is above the top, scroll up
            if (index < vsb.getValue()) {
                scrollVertical(index - vsb.getValue());
            }
            // If index is below the bottom, scroll down
            else if (index > lastItemDisplayed()) {
                int val = index - lastItemDisplayed();
                scrollVertical(val);
            }
        }
    }

    /**
     * clear
     */
    public void clear() {
        selected = new int[0];
        items = new Vector();
        currentIndex = -1;
        // Fixed 6291736: ITEM_STATE_CHANGED triggered after List.removeAll(), XToolkit
        // We should update 'focusIndex' variable more carefully
        setFocusIndex(-1);
        vsb.setValue(0);
        maxLength = 0;
        layout();
        repaint();
    }

    /**
     * return the selected indexes
     */
    public int[] getSelectedIndexes() {
        return selected;
    }

    /**
     * return the y value of the given index "i".
     * the y value represents the top of the text
     * NOTE: index can be larger than items.size as long
     * as it can fit the window
     */
    int index2y(int index) {
        int h = getItemHeight();

        //if (index < vsb.getValue() || index > vsb.getValue() + itemsInWindow()) {
        return MARGIN + ((index - vsb.getValue()) * h) + SPACE;
    }

    /* return true if the y is a valid y coordinate for
     *  a VISIBLE list item, otherwise returns false
     */
    boolean validY(int y) {

        int shown = itemsDisplayed();
        int lastY = shown * getItemHeight() + MARGIN;

        if (shown == itemsInWindow()) {
            lastY += MARGIN;
        }

        if (y < 0 || y >= lastY) {
            return false;
        }

        return true;
    }

    /**
     * return the position of the index in the selected array
     * if the index isn't in the array selected return -1;
     */
    int posInSel(int index) {
        for (int i = 0 ; i < selected.length ; i++) {
            if (index == selected[i]) {
                return i;
            }
        }
        return -1;
    }

    boolean isIndexDisplayed(int idx) {
        int lastDisplayed = lastItemDisplayed();

        return idx <= lastDisplayed &&
            idx >= Math.max(0, lastDisplayed - itemsInWindow() + 1);
    }

    /**
     * returns index of last item displayed in the List
     */
    int lastItemDisplayed() {
        int n = itemsInWindow();
        return (Math.min(items.size() - 1, (vsb.getValue() + n) - 1));
    }

    /**
     * returns whether the given index is currently scrolled off the top or
     * bottom of the List.
     */
    boolean isItemHidden(int index) {
        return index < vsb.getValue() ||
            index >= vsb.getValue() + itemsInWindow();
    }

    /**
     * returns the width of the list portion of the component (accounts for
     * presence of vertical scrollbar)
     */
    int getListWidth() {
        return vsbVis ? width - SCROLLBAR_AREA : width;
    }

    /**
     * returns number of  items actually displayed in the List
     */
    int itemsDisplayed() {

        return (Math.min(items.size()-vsb.getValue(), itemsInWindow()));

    }

    /**
     * scrollVertical
     * y is the number of items to scroll
     */
    void scrollVertical(int y) {
        if (log.isLoggable(PlatformLogger.Level.FINE)) {
            log.fine("Scrolling vertically by " + y);
        }
        int itemsInWin = itemsInWindow();
        int h = getItemHeight();
        int pixelsToScroll = y * h;

        if (vsb.getValue() < -y) {
            y = -vsb.getValue();
        }
        vsb.setValue(vsb.getValue() + y);

        Rectangle source = null;
        Point distance = null;
        int firstItem = 0, lastItem = 0;
        int options = PAINT_HIDEFOCUS | PAINT_ITEMS | PAINT_VSCROLL | PAINT_FOCUS;
        if (y > 0) {
            if (y < itemsInWin) {
                source = new Rectangle(MARGIN, MARGIN + pixelsToScroll, width - SCROLLBAR_AREA, h * (itemsInWin - y - 1)-1);
                distance = new Point(0, -pixelsToScroll);
                options |= COPY_AREA;
            }
            firstItem = vsb.getValue() + itemsInWin - y - 1;
            lastItem = vsb.getValue() + itemsInWin - 1;

        } else if (y < 0) {
            if (y + itemsInWindow() > 0) {
                source = new Rectangle(MARGIN, MARGIN, width - SCROLLBAR_AREA, h * (itemsInWin + y));
                distance = new Point(0, -pixelsToScroll);
                options |= COPY_AREA;
            }
            firstItem = vsb.getValue();
            lastItem = Math.min(getLastVisibleItem(), vsb.getValue() + -y);
        }
        repaint(firstItem, lastItem, options, source, distance);
    }

    /**
     * scrollHorizontal
     * x is the number of pixels to scroll
     */
    void scrollHorizontal(int x) {
        if (log.isLoggable(PlatformLogger.Level.FINE)) {
            log.fine("Scrolling horizontally by " + y);
        }
        int w = getListWidth();
        w -= ((2 * SPACE) + (2 * MARGIN));
        int h = height - (SCROLLBAR_AREA + (2 * MARGIN));
        hsb.setValue(hsb.getValue() + x);

        int options = PAINT_ITEMS | PAINT_HSCROLL;

        Rectangle source = null;
        Point distance = null;
        if (x < 0) {
            source = new Rectangle(MARGIN + SPACE, MARGIN, w + x, h);
            distance = new Point(-x, 0);
            options |= COPY_AREA;
        } else if (x > 0) {
            source = new Rectangle(MARGIN + SPACE + x, MARGIN, w - x, h);
            distance = new Point(-x, 0);
            options |= COPY_AREA;
        }
        repaint(vsb.getValue(), lastItemDisplayed(), options, source, distance);
    }

    /**
     * return the index
     */
    int y2index(int y) {
        if (!validY(y)) {
            return -1;
        }

        int i = (y - MARGIN) / getItemHeight() + vsb.getValue();
        int last = lastItemDisplayed();

        if (i > last) {
            i = last;
        }

        return i;

    }

    /**
     * is the index "index" selected
     */
    boolean isSelected(int index) {
        if (eventType == ItemEvent.SELECTED && index == eventIndex) {
            return true;
        }
        for (int i = 0 ; i < selected.length ; i++) {
            if (selected[i] == index) {
                return true;
            }
        }
        return false;
    }

    /**
     * return the number of items that can fit
     * in the current window
     */
    int itemsInWindow(boolean scrollbarVisible) {
        int h;
        if (scrollbarVisible) {
            h = height - ((2 * MARGIN) + SCROLLBAR_AREA);
        } else {
            h = height - 2*MARGIN;
        }
        return (h / getItemHeight());
    }

    int itemsInWindow() {
        return itemsInWindow(hsbVis);
    }

    /**
     * return true if the x and y position is in the horizontal scrollbar
     */
    boolean inHorizontalScrollbar(int x, int y) {
        int w = getListWidth();
        int h = height - SCROLLBAR_WIDTH;
        return (hsbVis &&  (x >= 0) && (x <= w) && (y > h));
    }

    /**
     * return true if the x and y position is in the verticalscrollbar
     */
    boolean inVerticalScrollbar(int x, int y) {
        int w = width - SCROLLBAR_WIDTH;
        int h = hsbVis ? height - SCROLLBAR_AREA : height;
        return (vsbVis && (x > w) && (y >= 0) && (y <= h));
    }

    /**
     * return true if the x and y position is in the window
     */
    boolean inWindow(int x, int y) {
        int w = getListWidth();
        int h = hsbVis ? height - SCROLLBAR_AREA : height;
        return ((x >= 0) && (x <= w)) && ((y >= 0) && (y <= h));
    }

    /**
     * return true if vertical scrollbar is visible and false otherwise;
     * hsbVisible is the visibility of the horizontal scrollbar
     */
    boolean vsbIsVisible(boolean hsbVisible){
        return (items.size() > itemsInWindow(hsbVisible));
    }

    /**
     * return true if horizontal scrollbar is visible and false otherwise;
     * vsbVisible is the visibility of the vertical scrollbar
     */
    boolean hsbIsVisible(boolean vsbVisible){
        int w = width - ((2*SPACE) + (2*MARGIN) + (vsbVisible ? SCROLLBAR_AREA : 0));
        return (maxLength > w);
    }

    /*
     * Returns true if the event has been handled and should not be
     * posted to Java
     */
    boolean prePostEvent(final AWTEvent e) {
        if (e instanceof MouseEvent) {
            return prePostMouseEvent((MouseEvent)e);
        }
        return super.prePostEvent(e);
    }

    /*
     * Fixed 6240151: XToolkit: Dragging the List scrollbar initiates DnD
     * To be compatible with Motif, MouseEvent originated on the scrollbar
     * should be sent into Java in this way:
     * - post: MOUSE_ENTERED, MOUSE_EXITED, MOUSE_MOVED
     * - don't post: MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, MOUSE_DRAGGED
     */
    boolean prePostMouseEvent(final MouseEvent me){
        if (getToplevelXWindow().isModalBlocked()) {
            return false;
        }

        int eventId = me.getID();

        if (eventId == MouseEvent.MOUSE_MOVED)
        {
            // only for performance improvement
        }else if((eventId == MouseEvent.MOUSE_DRAGGED ||
                  eventId == MouseEvent.MOUSE_RELEASED) &&
                 isScrollBarOriginated)
        {
            if (eventId == MouseEvent.MOUSE_RELEASED) {
                isScrollBarOriginated = false;
            }
            handleJavaMouseEventOnEDT(me);
            return true;
        }else if ((eventId == MouseEvent.MOUSE_PRESSED ||
                   eventId == MouseEvent.MOUSE_CLICKED) &&
                  (inVerticalScrollbar(me.getX(), me.getY()) ||
                   inHorizontalScrollbar(me.getX(), me.getY())))
        {
            if (eventId == MouseEvent.MOUSE_PRESSED) {
                isScrollBarOriginated = true;
            }
            handleJavaMouseEventOnEDT(me);
            return true;
        }
        return false;
    }

    /*
     * Do handleJavaMouseEvent on EDT
     */
    void handleJavaMouseEventOnEDT(final MouseEvent me){
        InvocationEvent ev = new InvocationEvent(target, new Runnable() {
            public void run() {
                handleJavaMouseEvent(me);
            }
        });
        postEvent(ev);
    }

    /*
     * Fixed 5010944: List's rows overlap one another
     * The bug is due to incorrent caching of the list item size
     * So we should recalculate font metrics on setFont
     */
    public void setFont(Font f) {
        if (!Objects.equals(getFont(), f)) {
            super.setFont(f);
            initFontMetrics();
            layout();
            repaint();
        }
    }

    /**
     * Sometimes painter is called on Toolkit thread, so the lock sequence is:
     *     awtLock -> Painter -> awtLock
     * Sometimes it is called on other threads:
     *     Painter -> awtLock
     * Since we can't guarantee the sequence, use awtLock.
     */
    class ListPainter {
        VolatileImage buffer;
        Color[] colors;

        private Color getListForeground() {
            if (fgColorSet) {
                return colors[FOREGROUND_COLOR];
            }
            else {
            return SystemColor.textText;
            }
        }
        private Color getListBackground() {
            if (bgColorSet) {
                return colors[BACKGROUND_COLOR];
            }
            else {
                return SystemColor.text;
            }
        }

        private Color getDisabledColor() {
            Color backgroundColor = getListBackground();
            Color foregroundColor = getListForeground();
            return (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();
        }

        private boolean createBuffer() {
            VolatileImage localBuffer = null;
            XToolkit.awtLock();
            try {
                localBuffer = buffer;
            } finally {
                XToolkit.awtUnlock();
            }

            if (localBuffer == null) {
                if (log.isLoggable(PlatformLogger.Level.FINE)) {
                    log.fine("Creating buffer " + width + "x" + height);
                }
                // use GraphicsConfig.cCVI() instead of Component.cVI(),
                // because the latter may cause a deadlock with the tree lock
                localBuffer =
                    graphicsConfig.createCompatibleVolatileImage(width+1,
                                                                 height+1);
            }
            XToolkit.awtLock();
            try {
                if (buffer == null) {
                    buffer = localBuffer;
                    return true;
                }
            } finally {
                XToolkit.awtUnlock();
            }
            return false;
        }

        public void invalidate() {
            XToolkit.awtLock();
            try {
                if (buffer != null) {
                    buffer.flush();
                }
                buffer = null;
            } finally {
                XToolkit.awtUnlock();
            }
        }

        private void paint(Graphics listG, int firstItem, int lastItem, int options) {
            paint(listG, firstItem, lastItem, options, null, null);
        }

        private void paint(Graphics listG, int firstItem, int lastItem, int options,
                           Rectangle source, Point distance) {
            if (log.isLoggable(PlatformLogger.Level.FINER)) {
                log.finer("Repaint from " + firstItem + " to " + lastItem + " options " + options);
            }
            if (firstItem > lastItem) {
                int t = lastItem;
                lastItem = firstItem;
                firstItem = t;
            }
            if (firstItem < 0) {
                firstItem = 0;
            }
            colors = getGUIcolors();
            VolatileImage localBuffer = null;
            do {
                XToolkit.awtLock();
                try {
                    if (createBuffer()) {
                        // First time created buffer should be painted over at full.
                        options = PAINT_ALL;
                    }
                    localBuffer = buffer;
                } finally {
                    XToolkit.awtUnlock();
                }
                switch (localBuffer.validate(getGraphicsConfiguration())) {
                  case VolatileImage.IMAGE_INCOMPATIBLE:
                      invalidate();
                      options = PAINT_ALL;
                      continue;
                  case VolatileImage.IMAGE_RESTORED:
                      options = PAINT_ALL;
                }
                Graphics g = localBuffer.createGraphics();

                // Note that the order of the following painting operations
                // should not be modified
                try {
                    g.setFont(getFont());

                    // hiding the focus rectangle must be done prior to copying
                    // area and so this is the first action to be performed
                    if ((options & (PAINT_HIDEFOCUS)) != 0) {
                        paintFocus(g, PAINT_HIDEFOCUS);
                    }
                    /*
                     * The shift of the component contents occurs while someone
                     * scrolls the component, the only purpose of the shift is to
                     * increase the painting performance. The shift should be done
                     * prior to painting any area (except hiding focus) and actually
                     * it should never be done jointly with erase background.
                     */
                    if ((options & COPY_AREA) != 0) {
                        g.copyArea(source.x, source.y, source.width, source.height,
                            distance.x, distance.y);
                    }
                    if ((options & PAINT_BACKGROUND) != 0) {
                        paintBackground(g);
                        // Since we made full erase update items
                        firstItem = getFirstVisibleItem();
                        lastItem = getLastVisibleItem();
                    }
                    if ((options & PAINT_ITEMS) != 0) {
                        paintItems(g, firstItem, lastItem, options);
                    }
                    if ((options & PAINT_VSCROLL) != 0 && vsbVis) {
                        g.setClip(getVScrollBarRec());
                        paintVerScrollbar(g, true);
                    }
                    if ((options & PAINT_HSCROLL) != 0 && hsbVis) {
                        g.setClip(getHScrollBarRec());
                        paintHorScrollbar(g, true);
                    }
                    if ((options & (PAINT_FOCUS)) != 0) {
                        paintFocus(g, PAINT_FOCUS);
                    }
                } finally {
                    g.dispose();
                }
            } while (localBuffer.contentsLost());
            listG.drawImage(localBuffer, 0, 0, null);
        }

        private void paintBackground(Graphics g) {
            g.setColor(SystemColor.window);
            g.fillRect(0, 0, width, height);
            g.setColor(getListBackground());
            g.fillRect(0, 0, listWidth, listHeight);
            draw3DRect(g, getSystemColors(), 0, 0, listWidth - 1, listHeight - 1, false);
        }

        private void paintItems(Graphics g, int firstItem, int lastItem, int options) {
            if (log.isLoggable(PlatformLogger.Level.FINER)) {
                log.finer("Painting items from " + firstItem + " to " + lastItem + ", focused " + focusIndex + ", first " + getFirstVisibleItem() + ", last " + getLastVisibleItem());
            }

            firstItem = Math.max(getFirstVisibleItem(), firstItem);
            if (firstItem > lastItem) {
                int t = lastItem;
                lastItem = firstItem;
                firstItem = t;
            }
            firstItem = Math.max(getFirstVisibleItem(), firstItem);
            lastItem = Math.min(lastItem, items.size()-1);

            if (log.isLoggable(PlatformLogger.Level.FINER)) {
                log.finer("Actually painting items from " + firstItem + " to " + lastItem +
                          ", items in window " + itemsInWindow());
            }
            for (int i = firstItem; i <= lastItem; i++) {
                paintItem(g, i);
            }
        }

        private void paintItem(Graphics g, int index) {
            if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                log.finest("Painting item " + index);
            }
            // 4895367 - only paint items which are visible
            if (!isItemHidden(index)) {
                Shape clip = g.getClip();
                int w = getItemWidth();
                int h = getItemHeight();
                int y = getItemY(index);
                int x = getItemX();
                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                    log.finest("Setting clip " + new Rectangle(x, y, w - (SPACE*2), h-(SPACE*2)));
                }
                g.setClip(x, y, w - (SPACE*2), h-(SPACE*2));

                // Always paint the background so that focus is unpainted in
                // multiselect mode
                if (isSelected(index)) {
                    if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                        log.finest("Painted item is selected");
                    }
                    g.setColor(getListForeground());
                } else {
                    g.setColor(getListBackground());
                }
                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                    log.finest("Filling " + new Rectangle(x, y, w, h));
                }
                g.fillRect(x, y, w, h);

                if (index <= getLastVisibleItem() && index < items.size()) {
                    if (!isEnabled()){
                        g.setColor(getDisabledColor());
                    } else if (isSelected(index)) {
                        g.setColor(getListBackground());
                    } else {
                        g.setColor(getListForeground());
                    }
                    String str = (String)items.elementAt(index);
                    g.drawString(str, x - hsb.getValue(), y + fontAscent);
                } else {
                    // Clear the remaining area around the item - focus area and the rest of border
                    g.setClip(x, y, listWidth, h);
                    g.setColor(getListBackground());
                    g.fillRect(x, y, listWidth, h);
                }
                g.setClip(clip);
            }
        }

        void paintScrollBar(XScrollbar scr, Graphics g, int x, int y, int width, int height, boolean paintAll) {
            if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                log.finest("Painting scrollbar " + scr + " width " +
                width + " height " + height + ", paintAll " + paintAll);
            }
            g.translate(x, y);
            scr.paint(g, getSystemColors(), paintAll);
            g.translate(-x, -y);
        }

        /**
         * Paint the horizontal scrollbar to the screen
         *
         * @param g the graphics context to draw into
         * @param colors the colors used to draw the scrollbar
         * @param paintAll paint the whole scrollbar if true, just the thumb if false
         */
        void paintHorScrollbar(Graphics g, boolean paintAll) {
            int w = getListWidth();
            paintScrollBar(hsb, g, 0, height - (SCROLLBAR_WIDTH), w, SCROLLBAR_WIDTH, paintAll);
        }

        /**
         * Paint the vertical scrollbar to the screen
         *
         * @param g the graphics context to draw into
         * @param colors the colors used to draw the scrollbar
         * @param paintAll paint the whole scrollbar if true, just the thumb if false
         */
        void paintVerScrollbar(Graphics g, boolean paintAll) {
            int h = height - (hsbVis ? (SCROLLBAR_AREA-2) : 0);
            paintScrollBar(vsb, g, width - SCROLLBAR_WIDTH, 0, SCROLLBAR_WIDTH - 2, h, paintAll);
        }


        private Rectangle prevFocusRect;
        private void paintFocus(Graphics g, int options) {
            boolean paintFocus = (options & PAINT_FOCUS) != 0;
            if (paintFocus && !hasFocus()) {
                paintFocus = false;
            }
            if (log.isLoggable(PlatformLogger.Level.FINE)) {
                log.fine("Painting focus, focus index " + getFocusIndex() + ", focus is " +
                         (isItemHidden(getFocusIndex())?("invisible"):("visible")) + ", paint focus is " + paintFocus);
            }
            Shape clip = g.getClip();
            g.setClip(0, 0, listWidth, listHeight);
            if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                log.finest("Setting focus clip " + new Rectangle(0, 0, listWidth, listHeight));
            }
            Rectangle rect = getFocusRect();
            if (prevFocusRect != null) {
                // Erase focus rect
                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                    log.finest("Erasing previous focus rect " + prevFocusRect);
                }
                g.setColor(getListBackground());
                g.drawRect(prevFocusRect.x, prevFocusRect.y, prevFocusRect.width, prevFocusRect.height);
                prevFocusRect = null;
            }
            if (paintFocus) {
                // Paint new
                if (log.isLoggable(PlatformLogger.Level.FINEST)) {
                    log.finest("Painting focus rect " + rect);
                }
                g.setColor(getListForeground());  // Focus color is always black on Linux
                g.drawRect(rect.x, rect.y, rect.width, rect.height);
                prevFocusRect = rect;
            }
            g.setClip(clip);
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java XListPeer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.