view usr/src/cmd/krb5/kadmin/gui/visualrt/sunsoft/jws/visual/rt/awt/WinScrollbar.java @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * ident	"%Z%%M%	%I%	%E% SMI"
 *
 *
 * Copyright (c) 1994-1995, 2001 by Sun Microsystems, Inc. 
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
 *
 *
 *        Copyright (C) 1996  Active Software, Inc.
 *                  All rights reserved.
 *
 * @(#) WinScrollbar.java 1.13 - last change made 05/02/97
 */

package sunsoft.jws.visual.rt.awt;

import sunsoft.jws.visual.rt.base.Global;
import sunsoft.jws.visual.rt.base.Util;
import java.awt.*;


public class WinScrollbar extends Canvas implements Runnable {
    
    /**
     * The horizontal Scrollbar variable.
     */
    public static final int	HORIZONTAL = Scrollbar.HORIZONTAL;
    
    /**
     * The vertical Scrollbar variable.
     */
    public static final int	VERTICAL   = Scrollbar.VERTICAL;
    
    /**
     * The value of the Scrollbar.
     */
    int	value;
    
    /**
     * The maximum value of the Scrollbar.
     */
    int	maximum;		// doesn't include the visible area
    
    /**
     * The minimum value of the Scrollbar.
     */
    int	minimum;
    
    /**
     * The size of the visible portion of the Scrollbar.
     */
    int	sVisible;
    
    /**
     * The Scrollbar's orientation--being either horizontal or vertical.
     */
    int	orientation;
    
    /**
     * The amount by which the scrollbar value will change when going
     * up or down by a line.
     */
    int lineIncrement = 1;
    
    /**
     * The amount by which the scrollbar value will change when going
     * up or down by a page.
     */
    int pageIncrement = 10;
    
    /**
     * Are we running on WindowsNT
     */
    private boolean winNT;
    
    private static WinScrollbar threadScrollbar;
    private static Thread scrollThread;
    private WinScrollbar currentScrollbar;
    private int currentScrollAction;
    private int currentScrollPosition;
    
    private static final int SCROLL_DELAY = 250;
    private static final int SCROLL_INTERVAL = 40;
    
    /**
     * Constructs a new vertical Scrollbar.
     */
    public WinScrollbar() {
        this(VERTICAL);
    }
    
    
    /**
     * Constructs a new Scrollbar with the specified orientation.
     * @param orientation either Scrollbar.HORIZONTAL 
     * or Scrollbar.VERTICAL
     * @exception IllegalArgumentException When an
     * illegal scrollbar orientation is given.
    */
    public WinScrollbar(int orientation) {
        switch (orientation) {
	case Scrollbar.HORIZONTAL:
	case Scrollbar.VERTICAL:
            this.orientation = orientation;
            break;
            
	default:
            /* JSTYLED */
	    throw new IllegalArgumentException(Global.getMsg("sunsoft.jws.visual.rt.awt.WinScrollbar.IllegalOrientation"));
        }
        
        winNT = Global.isWindowsNT();
    }
    
    /**
     * Constructs a new Scrollbar with the specified orientation,
     * value, page size,  and minumum and maximum values.
     * @param orientation either Scrollbar.HORIZONTAL 
     * or Scrollbar.VERTICAL
     * @param value the scrollbar's value
     * @param visible the size of the visible portion of the
     * scrollable area. The scrollbar will use this value when paging up
     * or down by a page.
     * @param minimum the minimum value of the scrollbar
     * @param maximum the maximum value of the scrollbar
     */
    public WinScrollbar(int orientation, int value, int visible,
			int minimum, int maximum) {
        this(orientation);
        setValues(value, visible, minimum, maximum);
    }
    
    /**
     * Returns the orientation for this Scrollbar.
     */
    public int getOrientation() {
        return orientation;
    }
    
    /**
     * Returns the current value of this Scrollbar.
     * @see #getMinimum
     * @see #getMaximum
     */
    public int getValue() {
        return value;
    }
    
    /**
     * Sets the value of this Scrollbar to the specified value.
     * @param value the new value of the Scrollbar. If this value is
     * below the current minimum or above 
     * the current maximum, it becomes the
     * new one of those values, respectively.
     * @see #getValue
     */
    public void setValue(int value) {
        if (value < minimum) {
            value = minimum;
        }
        if (value > (maximum - sVisible)) {
            value = maximum - sVisible;
        }
        if (value != this.value) {
            this.value = value;
            if (getPeer() != null)
                peerSetValue(value);
        }
    }
    
    /**
     * Returns the minimum value of this Scrollbar.
     * @see #getMaximum
     * @see #getValue
     */
    public int getMinimum() {
        return minimum;
    }
    
    /**
     * Returns the maximum value of this Scrollbar.
     * @see #getMinimum
     * @see #getValue
     */
    public int getMaximum() {
        return maximum;
    }
    
    /**
     * Returns the visible amount of the Scrollbar.
     */
    public int getVisible() {
        return sVisible;
    }
    
    /**
     * Sets the line increment for this scrollbar. This is the value
     * that will be added (subtracted) when the user hits the line down
     * (up) gadgets.
     */
    public void setLineIncrement(int l) {
        lineIncrement = l;
        if (getPeer() != null)
            peerSetLineIncrement(l);
    }
    
    /**
     * Gets the line increment for this scrollbar.
     */
    public int getLineIncrement() {
        return lineIncrement;
    }
    
    /**
     * Sets the page increment for this scrollbar. This is the value
     * that will be added (subtracted) when the user hits the page down
     * (up) gadgets.
     */
    public void setPageIncrement(int l) {
        pageIncrement = l;
        if (getPeer() != null)
            peerSetPageIncrement(l);
    }
    
    /**
     * Gets the page increment for this scrollbar.
     */
    public int getPageIncrement() {
        return pageIncrement;
    }
    
    /**
     * Sets the values for this Scrollbar.
     * @param value is the position in the current window.
     * @param visible is the amount visible per page
     * @param minimum is the minimum value of the scrollbar
     * @param maximum is the maximum value of the scrollbar
     */
    public void setValues(int value, int visible, int minimum,
			  int maximum) {
        if (visible < 0)
            visible = 0;
        
        if (visible > maximum)
            visible = maximum;
        
        if (maximum < minimum) {
            maximum = minimum;
        }
        if (value < minimum) {
            value = minimum;
        }
        if (value > (maximum - visible)) {
            value = (maximum - visible);
        }
        
        this.value = value;
        this.sVisible = visible;
        this.minimum = minimum;
        this.maximum = maximum;
        
        if (getPeer() != null)
            peerSetValues(value, sVisible, minimum, maximum);
    }
    
    /**
     * Returns the String parameters for this Scrollbar.
     */
    protected String paramString() {
        return super.paramString() +
	    /* NOI18N */",val=" + value +
	    /* NOI18N */",vis=" + isVisible() +
	    /* NOI18N */",min=" + minimum +
	    /* NOI18N */",max=" + maximum +
	    ((orientation == VERTICAL) ? /* NOI18N */
	    ",vert" : /* NOI18N */",horz");
    }
    
    /**
     * Returns the minimum size for the scrollbar
     */
    public Dimension minimumSize() {
        if (orientation == VERTICAL)
            return new Dimension(16, 50);
        else
            return new Dimension(50, 16);
    }
    
    /**
     * Returns the preferred size for the scrollbar
     */
    public Dimension preferredSize() {
        return minimumSize();
    }
    
    
    // The rest of this code does the things
    // that the peer would normally
    // if the peer weren't so badly broken.
    
    private Image buffer;
    private int prevWidth = 0;
    private int prevHeight = 0;
    private int action = 0;
    
    private int anchorPos;
    private int anchorValue;
    private int dragSpace;
    
    private static final int UP = 10;
    private static final int DOWN = 11;
    private static final int LEFT = 12;
    private static final int RIGHT = 13;
    
    private static final int LINEUP = 20;
    private static final int LINEDOWN = 21;
    private static final int PAGEUP = 22;
    private static final int PAGEDOWN = 23;
    private static final int DRAG = 24;
    
    private void peerSetValue(int value) {
        repaint();
    }
    
    private void peerSetLineIncrement(int l) {
    }
    
    private void peerSetPageIncrement(int l) {
    }
    
    private void peerSetValues(int value, int sVisible,
			       int minimum, int maximum) {
        repaint();
    }
    
    public void reshape(int x, int y, int width, int height) {
        super.reshape(x, y, width, height);
        
        if (prevWidth != width || prevHeight != height) {
            if (width > 0 && height > 0)
                buffer = createImage(width, height);
            else
                buffer = null;
            
            prevWidth = width;
            prevHeight = height;
        }
    }
    
    public void update(Graphics g) {
        if (Global.isWindows())
            g = getGraphics();
        draw(g);
    }
    
    public void paint(Graphics g) {
        if (Global.isWindows())
            g = getGraphics();
        draw(g);
    }
    
    private void draw(Graphics g) {
        if (buffer == null)
            return;
        
        drawScrollbar();
        g.drawImage(buffer, 0, 0, null);
    }
    
    private void drawScrollbar() {
        Graphics g = buffer.getGraphics();
        Dimension size = size();
        int w = size.width;
        int h = size.height;
        
        // Erase the old version
        g.setColor(getBackground());
        g.fillRect(0, 0, size.width, size.height);
        
        drawOutline(g, w-1, h-1);
        drawEndBoxes(g, w-1, h-1);
        
        int info[] = getDragBoxInfo();
        fillPageBox(g, w, h, info);
        drawDragBox(g, w-1, h-1, info);
    }
    
    private void drawOutline(Graphics g, int w, int h) {
        g.setColor(Global.util.darker(getBackground()));
        if (orientation == VERTICAL) {
            g.drawRect(0, 0, w, w);
            g.drawRect(0, w, w, h-2*w);
            g.drawRect(0, h-w, w, w);
        } else {
            g.drawRect(0, 0, h, h);
            g.drawRect(h, 0, w-2*h, h);
            g.drawRect(w-h, 0, h, h);
        }
    }
    
    private void drawEndBoxes(Graphics g, int w, int h) {
        if (orientation == VERTICAL) {
            if (action != LINEUP) {
                drawArrow(g, 0, 0, w, w, UP);
                drawBox(g, 0, 0, w, w);
            } else {
                drawArrow(g, 1, 1, w, w, UP);
            }
            
            if (action != LINEDOWN) {
                drawArrow(g, 0, h-w, w, w, DOWN);
                drawBox(g, 0, h-w, w, w);
            } else {
                drawArrow(g, 1, h-w+1, w, w, DOWN);
            }
        } else {
            if (action != LINEUP) {
                drawArrow(g, 0, 0, h, h, LEFT);
                drawBox(g, 0, 0, h, h);
            } else {
                drawArrow(g, 1, 1, h, h, LEFT);
            }
            
            if (action != LINEDOWN) {
                drawArrow(g, w-h, 0, h, h, RIGHT);
                drawBox(g, w-h, 0, h, h);
            } else {
                drawArrow(g, w-h+1, 1, h, h, RIGHT);
            }
        }
    }
    
    private void fillPageBox(Graphics g, int w, int h, int info[]) {
        g.setColor(pageDarker(getBackground()));
        if (orientation == VERTICAL) {
            if (action == PAGEUP) {
                g.fillRect(1, w, w-2, info[0]-w);
            } else if (action == PAGEDOWN) {
                g.fillRect(1, info[0]+info[1]+1, w-2,
			   h-(w+info[0]+info[1])-1);
            }
        } else {
            if (action == PAGEUP) {
                g.fillRect(h, 1, info[0]-h, h-2);
            } else if (action == PAGEDOWN) {
                g.fillRect(info[0]+info[1]+1, 1,
			   w-(h+info[0]+info[1])-1, h-2);
            }
        }
    }
    
    private void drawDragBox(Graphics g, int w, int h, int info[]) {
        if (orientation == VERTICAL) {
            drawBox(g, 0, info[0], w, info[1]);
        } else {
            drawBox(g, info[0], 0, info[1], h);
        }
    }
    
    private int [] getDragBoxInfo() {
        int info[] = new int[2];
        int minpix;
        int deltapix;
        Dimension size = size();
        
        if (orientation == VERTICAL) {
            minpix = size.width;
            deltapix = size.height - 2 * size.width;
        } else {
            minpix = size.height;
            deltapix = size.width - 2 * size.height;
        }
        
        int deltaval = maximum - minimum;
        double d = (double)deltapix/(double)deltaval;
        double xory = minpix + (value-minimum) * d;
        double worh = sVisible * d;
        
        info[0] = (int)xory;
        info[1] = (int)worh;
        
        return info;
    }
    
    private void drawBox(Graphics g, int x, int y, int w, int h) {
        g.setColor(getBackground());
        Global.util.draw3DRect(g, x, y, w, h,
			       Util.WIN95_WINDOW_BORDER, 2);
        
        if (true)
            return;
        else {
            g.setColor(Global.util.brighter(getBackground()));
            g.drawLine(x, y, x+w-1, y);
            g.drawLine(x, y, x, y+h-1);
            
            g.setColor(Color.white);
            g.drawLine(x+1, y+1, x+w-2, y+1);
            g.drawLine(x+1, y+1, x+1, y+h-2);
            
            g.setColor(Color.black);
            g.drawLine(x+w, y, x+w, y+h);
            g.drawLine(x, y+h, x+w, y+h);
            
            g.setColor(Global.util.darker(getBackground()));
            g.drawLine(x+w-1, y+1, x+w-1, y+h-1);
            g.drawLine(x+1, y+h-1, x+w-1, y+h-1);
        }
    }
    
    private void drawArrow(Graphics g, int x, int y, int w, int h,
			   int direction) {
        Polygon p = new Polygon();
        
        // xoff=4 and yoff=4 for the default case where w=15 and y=15
        int xoff = (w-3)/3;
        int yoff = (h-3)/3;
        int bd = 2;
        
        g.setColor(Color.black);
        
        switch (direction) {
	case LEFT:
            if (winNT) {
                x -= xoff/4;
                g.fillRect(x+bd+2*xoff-1, y+bd+(5*yoff/4)-1,
			   xoff/2+1, yoff/2+1);
            }
            p.addPoint(x+bd+xoff-1, y+bd+(3*yoff/2)-1);
            p.addPoint(x+bd+2*xoff-1, y+bd+yoff-3);
            p.addPoint(x+bd+2*xoff-1, y+bd+2*yoff+1);
            break;
            
	case RIGHT:
            if (winNT) {
                x += xoff/4+1;
                g.fillRect(x+bd+(xoff/2)-1, y+bd+(5*yoff/4)-1,
			   xoff/2+2, yoff/2+1);
            }
            p.addPoint(x+bd+xoff, y+bd+yoff-3);
            p.addPoint(x+bd+xoff*2, y+bd+(3*yoff/2)-1);
            p.addPoint(x+bd+xoff, y+bd+2*yoff+1);
            break;
            
	case UP:
            if (winNT) {
                y -= yoff/4+1;
                g.fillRect(x+bd+(5*xoff/4)-1, y+bd+2*yoff,
			   xoff/2+1, yoff/2+1);
            }
            p.addPoint(x+bd+xoff-3, y+bd+2*yoff);
            p.addPoint(x+bd+(3*xoff/2)-1, y+bd+yoff);
            p.addPoint(x+bd+(3*xoff/2), y+bd+yoff);
            p.addPoint(x+bd+2*xoff+2, y+bd+2*yoff);
            break;
            
	case DOWN:
            if (winNT) {
                y += yoff/4+1;
                g.fillRect(x+bd+(5*xoff/4)-1, y+bd+(yoff/2)-1,
			   xoff/2+1, yoff/2+2);
            }
            p.addPoint(x+bd+xoff-2, y+bd+yoff);
            p.addPoint(x+bd+2*xoff+1, y+bd+yoff);
            p.addPoint(x+bd+(3*xoff/2)-1, y+bd+2*yoff);
            p.addPoint(x+bd+(3*xoff/2)-1, y+bd+2*yoff-1);
            break;
        }
        
        g.fillPolygon(p);
    }
    
    private static final double PAGE_DFACTOR = 0.8;
    
    /**
     * Returns a darker version of this color used for the paging
     * highlight color.
     */
    private Color pageDarker(Color c) {
        return new Color(Math.max((int)(c.getRed()  *PAGE_DFACTOR), 0),
			 Math.max((int)(c.getGreen()*PAGE_DFACTOR), 0),
			 Math.max((int)(c.getBlue() *PAGE_DFACTOR), 0));
    }
    
    public boolean mouseDown(Event evt, int x, int y) {
        Dimension size = size();
        int w = size.width;
        int h = size.height;
        
        if (orientation == VERTICAL) {
            if (y < w) {
                lineUp(y);
            } else if (y >= (h-w)) {
                lineDown(y);
            } else {
                int info[] = getDragBoxInfo();
                if (y >= (w+1) && y < info[0]) {
                    pageUp(y);
                } else if (y >= info[0]+info[1] && y < (h-w)) {
                    pageDown(y);
                } else if (y >= info[0] && y < info[0]+info[1]) {
                    dragStart(x, y);
                }
            }
            
        } else {
            if (x < h) {
                lineUp(x);
            } else if (x >= (w-h)) {
                lineDown(x);
            } else {
                int info[] = getDragBoxInfo();
                if (x >= (h+1) && x < info[0]) {
                    pageUp(x);
                } else if (x >= info[0]+info[1] && x < (w-h)) {
                    pageDown(x);
                } else if (x >= info[0] && x < info[0]+info[1]) {
                    dragStart(x, y);
                }
            }
        }
        
        return false;
    }
    
    public boolean mouseDrag(Event evt, int x, int y) {
        if (action == DRAG) {
            drag(x, y);
            return true;
        } else if (threadScrollbar != null &&
		   threadScrollbar.currentScrollbar == this) {
            synchronized (threadScrollbar) {
                if (orientation == VERTICAL)
                    threadScrollbar.currentScrollPosition = y;
                else
                    threadScrollbar.currentScrollPosition = x;
            }
        }
        
        return false;
    }
    
    public boolean mouseUp(Event evt, int x, int y) {
        cancelAutoScroll();
        
        if (action == DRAG) {
            dragStop(x, y);
        }
        
        action = 0;
        repaint();
        
        return false;
    }
    
    private boolean lineUp(int pos) {
        boolean status = false;
        action = LINEUP;
        initAutoScroll(action, pos);
        
        int prevValue = value;
        value = Math.max(minimum, value-lineIncrement);
        if (value != prevValue) {
            status = true;
            postEvent(new Event(this, Event.SCROLL_LINE_UP,
				new Integer(value)));
        }
        
        repaint();
        return status;
    }
    
    private boolean lineDown(int pos) {
        boolean status = false;
        action = LINEDOWN;
        initAutoScroll(action, pos);
        
        int prevValue = value;
        value = Math.min(maximum-sVisible, value+lineIncrement);
        if (value != prevValue) {
            postEvent(new Event(this, Event.SCROLL_LINE_DOWN,
				new Integer(value)));
            status = true;
        }
        
        repaint();
        return status;
    }
    
    private boolean pageUp(int pos) {
        boolean status = false;
        action = PAGEUP;
        initAutoScroll(action, pos);
        
        int prevValue = value;
        value = Math.max(minimum, value-pageIncrement);
        if (value != prevValue) {
            status = true;
            postEvent(new Event(this, Event.SCROLL_PAGE_UP,
				new Integer(value)));
        }
        
        repaint();
        return status;
    }
    
    private boolean pageDown(int pos) {
        boolean status = false;
        action = PAGEDOWN;
        initAutoScroll(action, pos);
        
        int prevValue = value;
        value = Math.min(maximum-sVisible, value+pageIncrement);
        if (value != prevValue) {
            status = true;
            postEvent(new Event(this, Event.SCROLL_PAGE_DOWN,
				new Integer(value)));
        }
        
        repaint();
        return status;
    }
    
    private void dragStart(int x, int y) {
        action = DRAG;
        
        if (orientation == VERTICAL)
            anchorPos = y;
        else
            anchorPos = x;
        
        anchorValue = value;
        
        Dimension size = size();
        int info[] = getDragBoxInfo();
        
        if (orientation == VERTICAL)
            dragSpace = size.height - size.width*2 - info[1];
        else
            dragSpace = size.width - size.height*2 - info[1];
    }
    
    private void drag(int x, int y) {
        if (orientation == VERTICAL)
            newDragValue(y);
        else
            newDragValue(x);
    }
    
    private void dragStop(int x, int y) {
        action = 0;
        drag(x, y);
    }
    
    private void newDragValue(int pos) {
        int pixelsDiff = pos - anchorPos;
        int valDiff = (pixelsDiff * (maximum-minimum) / dragSpace);
        int prevValue = value;
        
        value = anchorValue + valDiff;
        if (valDiff < 0)
            value = Math.max(value, minimum);
        else
            value = Math.min(value, maximum-sVisible);
        
        if (value != prevValue)
            postEvent(new Event(this, Event.SCROLL_ABSOLUTE,
				new Integer(value)));
        
        repaint();
    }
    
    private void initAutoScroll(int action, int pos) {
        if (Thread.currentThread() == scrollThread)
            return;
        
        if (threadScrollbar == null) {
            threadScrollbar = this;
            scrollThread = new Thread(threadScrollbar, /* NOI18N */
				      "WindowsScrollbarThread");
            scrollThread.setDaemon(true);
            scrollThread.start();
        }
        
        synchronized (threadScrollbar) {
            threadScrollbar.currentScrollbar = this;
            threadScrollbar.currentScrollAction = action;
            threadScrollbar.currentScrollPosition = pos;
            threadScrollbar.notify();
        }
    }
    
    private void cancelAutoScroll() {
        if (threadScrollbar != null) {
            synchronized (threadScrollbar) {
                threadScrollbar.currentScrollbar = null;
                threadScrollbar.currentScrollAction = -1;
                threadScrollbar.currentScrollPosition = -1;
                threadScrollbar.notify();
            }
        }
    }
    
    public synchronized void run() {
        boolean scrolling = false;
        long waitTime;
        
        while (scrollThread == Thread.currentThread()) {
            long startTime = System.currentTimeMillis();
            
            if (currentScrollbar == null) {
                waitTime = 0;
                scrolling = false;
            } else {
                if (scrolling) {
                    if (!doScroll(currentScrollbar,
				  currentScrollAction,
				  currentScrollPosition)) {
                        cancelAutoScroll();
                        waitTime = 0;
                    } else {
                        waitTime = SCROLL_INTERVAL;
                    }
                } else {
                    waitTime = SCROLL_DELAY;
                    scrolling = true;
                }
            }
            
            // Wait for "waitTime" milliseconds.
            // But if "waitTime" is zero,
            // then just wait for a notify.  If
            // the currentScrollbar changes,
            // then don't wait any longer.
            if (waitTime == 0 || currentScrollbar == null) {
                try { wait(0); }
                catch (InterruptedException ex) {}
            } else {
                WinScrollbar initScrollbar = currentScrollbar;
                long targetTime = startTime + waitTime;
                long diff = targetTime - System.currentTimeMillis();
                
                while (currentScrollbar == initScrollbar && diff > 0) {
                    try { wait(diff); }
                    catch (InterruptedException ex) {}
                    diff = targetTime - System.currentTimeMillis();
                }
                
                if (currentScrollbar != initScrollbar)
                    scrolling = false;
            }
        }
    }
    
    private boolean doScroll(WinScrollbar scrollbar,
			     int action, int pos) {
        boolean status = false;
        
        switch (action) {
	case LINEUP:
            status = scrollbar.lineUp(pos);
            break;
            
	case LINEDOWN:
            status = scrollbar.lineDown(pos);
            break;
            
	case PAGEUP:
            if (continuePaging(scrollbar, action, pos)) {
                status = scrollbar.pageUp(pos);
            } else {
                // Keep trying to scroll for the case
                // where the user drags
                // the mouse while paging.  We want to
                // track the drag position
                // in the direction of the original paging action.
                status = true;
            }
            break;
            
	case PAGEDOWN:
            if (continuePaging(scrollbar, action, pos)) {
                status = scrollbar.pageDown(pos);
            } else {
                // Keep trying to scroll for the case
                // where the user drags
                // the mouse while paging.  We want to
                // track the drag position
                // in the direction of the original paging action.
                status = true;
            }
            break;
            
	default:
            break;
        }
        
        return status;
    }
    
    private boolean continuePaging(WinScrollbar scrollbar, int action,
				   int pos) {
        boolean status = false;
        int info[] = scrollbar.getDragBoxInfo();
        
        if (pos < info[0]) {
            status = (action == PAGEUP);
        } else if (pos >= info[0]+info[1]) {
            status = (action == PAGEDOWN);
        }
        
        return status;
    }
}