Package java.awt

Source Code of java.awt.ChoicePopupBox$ChoiceScrollable

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package java.awt;

import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import org.apache.harmony.awt.ChoiceStyle;
import org.apache.harmony.awt.PeriodicTimer;
import org.apache.harmony.awt.ScrollStateController;
import org.apache.harmony.awt.Scrollable;
import org.apache.harmony.awt.ScrollbarStateController;
import org.apache.harmony.awt.gl.MultiRectArea;
import org.apache.harmony.awt.state.ListState;

/**
* Helper class: popup window containing list of
* Choice items, implements popup and scrolling list behavior
* of Choice component
*/
class ChoicePopupBox extends PopupBox {
    private final Choice choice;

    /**
     * how many items to scroll on PgUp/PgDn
     */
    final static int PAGE_SIZE = 8;

    /**
     * how many ticks to skip when scroll speed is minimum
     */
    final static int MAX_SKIP = 12;

    int selectedItem;

    int focusedItem;

    /**
     * fields for dragging/scrolling behavior implementation
     */
    private int scrollLocation;

    transient ScrollPaneAdjustable vAdj;

    Scrollable scrollable;

    ScrollStateController scrollController;

    ScrollbarStateController scrollbarController;

    boolean scroll;

    private boolean scrollDragged;

    /**
     * fields for scrolling animation
     */
    private final PeriodicTimer dragScrollTimer;

    private int dragScrollSpeed;

    private Runnable dragScrollHandler;

    private int nSkip; // scroll every nSkip ticks

    private final ChoiceListState listState;

    private final ChoiceStyle style;

    /**
     * List state implementation
     */
    class ChoiceListState extends Choice.ComponentState implements ListState {
        ChoiceListState() {
            choice.super();
        }

        public Rectangle getItemBounds(int idx) {
            return ChoicePopupBox.this.getItemBounds(idx);
        }

        public Rectangle getClient() {
            Rectangle clientRect = ChoicePopupBox.this.getBounds();
            if (scroll) {
                clientRect.width -= scrollable.getAdjustableWidth();
            }
            clientRect.grow(-1, -1);
            return clientRect;
        }

        public int getItemCount() {
            return choice.getItemCount();
        }

        public boolean isSelected(int idx) {
            return (selectedItem == idx);
        }

        public String getItem(int idx) {
            return choice.getItem(idx);
        }

        public int getCurrentIndex() {
            return focusedItem;
        }

        @Override
        public Dimension getSize() {
            return ChoicePopupBox.this.getSize();
        }
    }

    /**
     * Scrolling behavior implementation
     */
    class ChoiceScrollable implements Scrollable {
        public Adjustable getVAdjustable() {
            return vAdj;
        }

        public Adjustable getHAdjustable() {
            return null;
        }

        public Insets getInsets() {
            return Choice.INSETS;
        }

        public Point getLocation() {
            return new Point(0, scrollLocation);
        }

        public void setLocation(Point p) {
            scrollLocation = p.y;
        }

        public Component getComponent() {
            return choice;
        }

        public Dimension getSize() {
            return new Dimension(getWidth(), choice.getItemHeight() * choice.getItemCount());
        }

        public void doRepaint() {
            repaint();
        }

        public int getAdjustableWidth() {
            return vAdj.getBounds().width;
        }

        public int getAdjustableHeight() {
            return 0;
        }

        public void setAdjustableSizes(Adjustable adj, int vis, int min, int max) {
            vAdj.setSizes(vis, min, max);
        }

        public int getAdjustableMode(Adjustable adj) {
            return Scrollable.VERTICAL_ONLY;
        }

        public void setAdjustableBounds(Adjustable adj, Rectangle r) {
            vAdj.setBounds(r);
        }

        public int getWidth() {
            return ChoicePopupBox.this.getWidth();
        }

        public int getHeight() {
            return ChoicePopupBox.this.getHeight();
        }

        public void doRepaint(Rectangle r) {
            repaint(r);
        }
    }

    public ChoicePopupBox(Choice choice) {
        scrollLocation = 0;
        this.choice = choice;
        style = choice.popupStyle;
        vAdj = new ScrollPaneAdjustable(choice, Adjustable.VERTICAL) {
            private static final long serialVersionUID = 2280561825998835980L;

            @Override
            void repaintComponent(Rectangle r) {
                if (isVisible()) {
                    repaint(r);
                }
            }
        };
        scrollable = new ChoiceScrollable();
        listState = new ChoiceListState();
        scrollController = new ScrollStateController(scrollable);
        scrollbarController = vAdj.getStateController();
        vAdj.addAdjustmentListener(scrollController);
        nSkip = MAX_SKIP;
        dragScrollTimer = new PeriodicTimer(25l, getAsyncHandler(getDragScrollHandler()));
    }

    public int getWidth() {
        return getSize().width;
    }

    /**    
     * @return action to be performed on scrolling by mouse drag:
     * action just highlights("focuses") next/previous item
     */
    private Runnable getDragScrollHandler() {
        if (dragScrollHandler == null) {
            dragScrollHandler = new Runnable() {
                int n;

                public void run() {
                    if (n++ % nSkip == 0) {
                        int selItem = focusedItem
                                + (dragScrollSpeed / Math.abs(dragScrollSpeed));
                        selectItem(selItem, false);
                        vAdj.setValue(vAdj.getValue() + dragScrollSpeed);
                    }
                }
            };
        }
        return dragScrollHandler;
    }

    /**
     * Makes handler asynchronous
     * @param handler any Runnable
     * @return asynchronous Runnable: just invokes <code> handler.run()</code>
     *  on event dispatch thread
     */
    private Runnable getAsyncHandler(final Runnable handler) {
        return new Runnable() {
            public void run() {
                EventQueue.invokeLater(handler);
            }
        };
    }

    /**
     * Paints popup list and vertical scrollbar(if necessary)
     */
    @Override
    void paint(Graphics g) {
        toolkit.theme.drawList(g, listState, true);
        if (scroll) {
            Rectangle r = vAdj.getBounds();
            Rectangle oldClip = g.getClipBounds();
            g.setClip(r.x, r.y, r.width, r.height);
            vAdj.prepaint(g);
            g.setClip(oldClip);
        }
    }

    /**
     * Gets item bounds inside popup list
     * @param pos item index
     * @return item bounds rectangle relative to
     * popup list window origin
     */
    Rectangle getItemBounds(int pos) {
        int itemHeight = choice.getItemHeight();
        Point p = new Point(0, pos * itemHeight);
        Rectangle itemRect = new Rectangle(p, new Dimension(getWidth(), itemHeight));
        itemRect.translate(0, scrollLocation);
        return itemRect;
    }

    /**
     * Calculates popup window screen bounds
     * using choice style and location on screen
     * @return list popup window bounds in screen coordinates
     */
    Rectangle calcBounds() {
        scroll = false;
        int itemHeight = choice.getItemHeight();
        int choiceWidth = choice.getWidth();
        int itemWidth = style.getPopupWidth(choiceWidth);
        int count = choice.getItemCount();
        if (count > PAGE_SIZE) {
            count = PAGE_SIZE;
            scroll = true;
        }
        int height = count * itemHeight;
        Rectangle screenBounds = choice.getGraphicsConfiguration().getBounds();
        Point screenLoc = choice.getLocationOnScreen();
        int x = style.getPopupX(screenLoc.x, itemWidth, choiceWidth, screenBounds.width);
        int y = calcY(screenLoc.y, height, screenBounds.height);
        return new Rectangle(x, y, itemWidth, height);
    }

    /**
     * Places list popup window below or above Choice component
     * @param y Choice component screen y-coordinate
     * @param height list height
     * @param screenHeight height of entire screen
     * @return y screen coordinate of list popup window
     */
    int calcY(int y, int height, int screenHeight) {
        int h = choice.getHeight();
        y += h; // popup is below choice
        int maxHeight = screenHeight - y;
        if (height > maxHeight) {
            // popup is above choice
            y -= height + h;
        }
        return y;
    }

    void show() {
        Rectangle r = calcBounds();
        selectedItem = choice.getSelectedIndex();
        show(r.getLocation(), r.getSize(), choice.getWindowAncestor());
        if (scroll) {
            vAdj.setUnitIncrement(choice.getItemHeight());
            vAdj.setBlockIncrement(vAdj.getUnitIncrement() * PAGE_SIZE);
            scrollController.layoutScrollbars();
            makeFirst(choice.selectedIndex);
        }
        getNativeWindow().setAlwaysOnTop(true);
    }

    /**
     * @return current popup window bounds
     */
    Rectangle getBounds() {
        return new Rectangle(new Point(), getSize());
    }

    @Override
    void onKeyEvent(int eventId, int vKey, long when, int modifiers) {
        if (eventId == KeyEvent.KEY_PRESSED) {
            switch (vKey) {
                case KeyEvent.VK_ESCAPE:
                    hide();
                    break;
                case KeyEvent.VK_ENTER:
                    hide();
                    choice.selectAndFire(selectedItem);
                    break;
            }
        }
    }

    /**
     * Moves selection <code>incr</code> items up/down from current selected
     * item, scrolls list to the new selected item
     */
    void changeSelection(int incr) {
        int newIndex = choice.getValidIndex(selectedItem + incr);
        makeFirst(newIndex);
        selectItem(newIndex);
    }

    /**
     * Scrolls list to make item the first visible item
     * @param idx index of item to scroll to(make visible)
     */
    void makeFirst(int idx) {
        if (scroll) {
            vAdj.setValue(getItemBounds(idx).y - scrollLocation);
        }
    }

    /**
     * Scrolls list to specified y position
     */
    void dragScroll(int y) {
        if (!scroll) {
            return;
        }
        int h = getHeight();
        if ((y >= 0) && (y < h)) {
            dragScrollTimer.stop();
            return;
        }
        int itemHeight = choice.getItemHeight();
        int dy = ((y < 0) ? -y : (y - h));
        nSkip = Math.max(1, MAX_SKIP - 5 * dy / itemHeight);
        dragScrollSpeed = itemHeight * (y / Math.abs(y));
        dragScrollTimer.start();
    }

    /**
     * Handles all mouse events on popup window
     */
    @Override
    void onMouseEvent(int eventId, Point where, int mouseButton, long when, int modifiers,
            int wheelRotation) {
        if (scroll && (eventId == MouseEvent.MOUSE_WHEEL)) {
            processMouseWheel(where, when, modifiers, wheelRotation);
            return;
        }
        if (scroll && (vAdj.getBounds().contains(where) || scrollDragged)) {
            processMouseEvent(eventId, where, mouseButton, when, modifiers);
            return;
        }
        boolean button1 = (mouseButton == MouseEvent.BUTTON1);
        int y = where.y;
        int index = (y - scrollLocation) / choice.getItemHeight();
        switch (eventId) {
            case MouseEvent.MOUSE_PRESSED:
                break;
            case MouseEvent.MOUSE_DRAGGED:
                // scroll on mouse drag
                if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0) {
                    dragScroll(y);
                }
            case MouseEvent.MOUSE_MOVED:
                if ((index < 0) || (index >= choice.getItemCount()) || (index == selectedItem)) {
                    return;
                }
                boolean select = getBounds().contains(where);
                if ((y >= 0) && (y < getHeight())) {
                    selectItem(index, select); //hot track
                }
                break;
            case MouseEvent.MOUSE_RELEASED:
                if (button1) {
                    hide();
                    choice.selectAndFire(index);
                }
                break;
        }
    }

    /**
     * Mouse wheel event processing by vertical scrollbar
     */
    private void processMouseWheel(Point where, long when, int modifiers, int wheelRotation) {
        MouseWheelEvent mwe = new MouseWheelEvent(choice, MouseEvent.MOUSE_WHEEL, when,
                modifiers, where.x, where.y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1,
                wheelRotation);
        scrollController.mouseWheelMoved(mwe);
    }

    /**
     * Mouse event processing by vertical scrollbar
     */
    private void processMouseEvent(int id, Point where, int button, long when, int modifiers) {
        MouseEvent me = new MouseEvent(choice, id, when, modifiers, where.x, where.y, 0, false,
                button);
        switch (id) {
            case MouseEvent.MOUSE_PRESSED:
                scrollDragged = true;
                scrollbarController.mousePressed(me);
                break;
            case MouseEvent.MOUSE_RELEASED:
                scrollDragged = false;
                scrollbarController.mouseReleased(me);
                break;
            case MouseEvent.MOUSE_DRAGGED:
                scrollbarController.mouseDragged(me);
                break;
        }
    }

    /**
     * Selects/focuses item and repaints popup window
     * @param index item to be focused/selected
     * @param highlight item is highlighted(selected) if true,
     * only focused otherwise
     */
    private void selectItem(int index, boolean highlight) {
        Rectangle oldRect = listState.getItemBounds(focusedItem);
        if (focusedItem != selectedItem) {
            oldRect = oldRect.union(listState.getItemBounds(selectedItem));
        }
        focusedItem = index;
        selectedItem = (highlight ? focusedItem : -1);
        Rectangle itemRect = listState.getItemBounds(index);
        Rectangle paintRect = itemRect.union(oldRect);
        paintRect.grow(0, 2);
        if (scroll) {
            paintRect.width -= vAdj.getBounds().width;
        }
        repaint(paintRect);
    }

    private void selectItem(int index) {
        selectItem(index, true);
    }

    void repaint(final Rectangle r) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                paint(new MultiRectArea(r));
            }
        });
    }

    void repaint() {
        repaint(null);
    }

    @Override
    boolean closeOnUngrab(Point start, Point end) {
        if (!getBounds().contains(end)) {
            // close on mouse ungrab only
            // if grab started not above
            // the scrollbar
            return !vAdj.getBounds().contains(start);
        }
        return true;
    }

    @Override
    void hide() {
        // stop timer before closing:
        if (scroll && dragScrollTimer.isRunning()) {
            dragScrollTimer.stop();
        }
        super.hide();
    }
}
TOP

Related Classes of java.awt.ChoicePopupBox$ChoiceScrollable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.