home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / VCafe / prosrc.bin / Scroller.java < prev    next >
Encoding:
Java Source  |  1998-03-18  |  46.6 KB  |  1,547 lines

  1. /*
  2.  * Copyright (c) 1996 Krumel & Associates, Inc. All Rights Reserved.
  3.  *
  4.  * www.krumel.com - controls@krumel.com
  5.  *
  6.  * Permission is given to the buyer of this package for one software
  7.  * developer to use this software on one CPU (one workstation) and to make
  8.  * one backup copy.  You may uitilize and/or midfy this class for use in your
  9.  * projects.  You may distribute or sell any executable which results from
  10.  * using this code in yur application, except a utility or class of similar
  11.  * nature to this product.  You may distribute this this product in compiled
  12.  * form only, but soley to be used with your cmpiled executable product
  13.  * for the puposes of dynamic loading. You may NOT redistribute the source
  14.  * code in any form or make it accessible through a network or other
  15.  * distribution media to others. Please refer to the file "copyright.html"
  16.  * for further important copyright and licensing information.
  17.  *
  18.  * The source code is the confidential and proprietary information
  19.  * of Krumel & Associates, Inc. ("Confidential Information").  You shall
  20.  * not disclose such Confidential Information and shall use it only in
  21.  * accordance with the terms of the license agreement you entered into
  22.  * with Krumel & Associates, Inc..
  23.  
  24.  * KRUMEL & ASSOCIATES MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
  25.  * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
  26.  * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  27.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KRUMEL & ASSOCIATES SHALL NOT
  28.  * BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
  29.  * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  30.  */
  31.  
  32. package symantec.itools.db.awt.awt;
  33.  
  34. import java.awt.*;
  35. import java.util.*;
  36. import symantec.itools.db.awt.genutil.*;
  37. import java.awt.event.*;
  38.  
  39. /**
  40.  * A scrollbar implemented entirely in Java.
  41.  */
  42. public class Scroller extends Canvas
  43.                       implements Observer, Notifiable, Adjustable {
  44.     /**
  45.      * The background color for the scrollbar (where buttons and slider are not located).
  46.      */
  47.     Color bg = new Color(220, 220, 220);
  48.  
  49.     /**
  50.      * The foregound color for the scrollbar (color of button faces).
  51.      */
  52.     Color fg = Color.lightGray;
  53.  
  54.     /**
  55.      * The highlight color for the raised portion that gets the light.
  56.      */
  57.     Color upHl = Color.white;
  58.  
  59.     /**
  60.      * The highlight color for the raised portion of a button that gets the shadow.
  61.      */
  62.     Color downHl = Color.gray;
  63.  
  64.     /**
  65.      * The color of the border around the scrollbar.
  66.      */
  67.     Color borderColor = Color.gray;
  68.  
  69.     /**
  70.      * Color used to draw border around buttons
  71.      */
  72.     Color shapeColor = Color.black;
  73.  
  74.     /**
  75.      * Value for whether a button is currently being depressed and down.
  76.      */
  77.     boolean buttonDown;
  78.  
  79.     /**
  80.      * Which button is in the pushed state, if any
  81.      */
  82.     int         buttonPushed = NONE;
  83.  
  84.     /**
  85.      * The minimum value of the Scroller.
  86.      */
  87.     int         min;
  88.     /**
  89.      * The maximum value of the Scroller.
  90.      */
  91.     int         max;
  92.  
  93.     /**
  94.      * The size of the visible portion of the Scroller.
  95.      */
  96.     int         thumbSize;
  97.     /**
  98.      * The current position of the thumb.
  99.      */
  100.     int         currentPos;
  101.  
  102.     /**
  103.      * The Scroller's orientation--being either horizontal or vertical.
  104.      */
  105.     int    orientation;
  106.  
  107.     /**
  108.      * The amount by which the Scroller value will change when going
  109.      * up or down by a page.
  110.      */
  111.     int pageIncrement = 10;
  112.  
  113.  
  114.     /**
  115.      * The amount by which the Scroller value will change when going
  116.      * up or down by a line.
  117.      */
  118.     int lineIncrement = 1;
  119.  
  120.     /**
  121.      * Offscreen image used to draw Scroller
  122.      */
  123.     Image image;
  124.  
  125.     /**
  126.      * The graphics context for the offscreen image
  127.      */
  128.     Graphics imageG;
  129.  
  130.     /**
  131.      * The size of buttons and scrollbar width/height.
  132.      */
  133.     int scroll_ht = DEFAULT_SCROLL_HT;
  134.  
  135.     Timer   timer = Timer.getTimer();
  136.  
  137.     transient AdjustmentListener adjustmentListener;
  138.  
  139.     /**
  140.      * Synchronization lock independent of AWT
  141.      */
  142.     protected final Object LOCK = new Object();
  143.  
  144.     static final int BASE = 555555;
  145.  
  146.     /**
  147.      * Event ID for when scrolling is initiated by the user.  The following events
  148.      * be any standard scroll event defined in the Event class.  When all scrolling
  149.      * ceases, an END_SCROLL event will be propagated through the containment
  150.      * hierarchy.
  151.      */
  152.     public static final int BEGIN_SCROLL = BASE + 1;
  153.  
  154.     /**
  155.      * Event ID for when a series of scrolling events initiated by the user are
  156.      * ceased.  Always preceeded by a BEGIN_SCROLL event and at least one scroll
  157.      * event as defined in the Event class.
  158.      */
  159.     public static final int END_SCROLL = BASE + 2;
  160.  
  161.     /**
  162.      * The default size of buttons and scrollbar width/height.
  163.      */
  164.     public static int DEFAULT_SCROLL_HT = 17;
  165.  
  166.     //button constants
  167.     static final int NONE = -1;
  168.     static final int LEFT = 0;
  169.     static final int RIGHT = 1;
  170.     static final int UP = 2;
  171.     static final int DOWN = 3;
  172.     static final int SLIDER = 4;
  173.  
  174.  
  175.     /**
  176.      * Constructs a new vertical Scroller.
  177.      */
  178.     public Scroller() {
  179.         this(VERTICAL);
  180.     }
  181.  
  182.     /**
  183.      * Constructs a new Scroller with the specified orientation.
  184.      * @param orientation either Scroller.HORIZONTAL or Scroller.VERTICAL
  185.      * @exception IllegalArgumentException When an illegal Scroller orientation is given.
  186.      */
  187.     public Scroller(int orientation) {
  188.         //enable mouse events so java.awt.Component will forward them to us.
  189.         enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
  190.         switch (orientation) {
  191.             case Scroller.HORIZONTAL:
  192.             case Scroller.VERTICAL:
  193.                 this.orientation = orientation;
  194.                 break;
  195.             default:
  196.                 throw new IllegalArgumentException("illegal Scroller orientation");
  197.         }
  198.         timer.register(this, 100);
  199.     }
  200.  
  201.     /**
  202.      * Constructs a new Scroller with the specified orientation,
  203.      * value, page size,  and minumum and maximum values.
  204.      * @param orientation either Scroller.HORIZONTAL or Scroller.VERTICAL
  205.      * @param value the Scroller's value
  206.      * @param visible the size of the visible portion of the
  207.      * scrollable area. The Scroller will use this value when paging up
  208.      * or down by a page.
  209.      * @param minimum the minimum value of the Scroller
  210.      * @param maximum the maximum value of the Scroller
  211.      */
  212.     public Scroller(int orientation, int value, int visible, int minimum, int maximum) {
  213.         this(orientation);
  214.         setValues(value, visible, minimum, maximum);
  215.     }
  216.  
  217.     /**
  218.      * Returns the orientation for this Scroller.
  219.      */
  220.     public int getOrientation() {
  221.         return orientation;
  222.     }
  223.  
  224.     /**
  225.      * Returns the current value of this Scroller.
  226.      * @see #getMinimum
  227.      * @see #getMaximum
  228.      */
  229.     public int getValue() {
  230.         return currentPos;
  231.     }
  232.  
  233.     /**
  234.      * Sets the value of this Scroller to the specified value.
  235.      * @param value the new value of the Scroller. If this value is
  236.      * below the current minimum or above the current maximum, it becomes the
  237.      * new one of those values, respectively.
  238.      * @see #getValue
  239.      */
  240.     public void setValue(int value) {
  241.         if (value < min) {
  242.             value = min;
  243.         }
  244.  
  245.         if (value > max-thumbSize+1) {
  246.             value = max-thumbSize+1;
  247.         }
  248.  
  249.         if (value != currentPos) {
  250.             currentPos = value;
  251.             redoSliderRect();
  252.             redrawAsync();
  253.         }
  254.     }
  255.  
  256.     /**
  257.      * Sets the maximum sideways dimension of the scroll buttons.
  258.      */
  259.     public void setButtonHeight(int pixels) {
  260.         scroll_ht = pixels;
  261.     }
  262.  
  263.     /**
  264.      * Gets the maximum sideways dimension of the scroll buttons.
  265.      */
  266.     public int getButtonHeight() {
  267.         return scroll_ht;
  268.     }
  269.  
  270.     /**
  271.      * Prints a values of the scroller to standard out.
  272.      */
  273.     public void printString() {
  274.         System.out.println(toString());
  275.     }
  276.  
  277.     /**
  278.      *  Inherited from java.lang.Object.
  279.      */
  280.     public String toString() {
  281.         return "symantec.itools.db.awt.Scroller: value="+currentPos +" min="+min+" max="+max+" currPos="+currentPos
  282.                 + " thumbSize=" + thumbSize
  283.                 + " orientation="+((orientation==HORIZONTAL) ?"HORIZONTAL" :"VERTICAL");
  284.     }
  285.  
  286.     /**
  287.      * Returns the minimum value of this Scroller.
  288.      * @see #getMaximum
  289.      * @see #getValue
  290.      */
  291.     public int getMinimum() {
  292.         return min;
  293.     }
  294.  
  295.     /**
  296.      * Returns the maximum value of this Scroller.
  297.      * @see #getMinimum
  298.      * @see #getValue
  299.      */
  300.     public int getMaximum() {
  301.         return max;
  302.     }
  303.  
  304.     /**
  305.      * Returns the visible amount of the Scroller.
  306.      */
  307.     public int getVisible() {
  308.         return thumbSize;
  309.     }
  310.  
  311.     /**
  312.      * Sets the line increment for this Scroller. This is the value
  313.      * that will be added (subtracted) when the user hits the line down
  314.      * (up) gadgets.
  315.      */
  316.     public void setLineIncrement(int l) {
  317.         if (l != lineIncrement) {
  318.             lineIncrement = l;
  319.             redoSliderRect();
  320.             redrawAsync();
  321.         }
  322.     }
  323.  
  324.     /**
  325.      * Gets the line increment for this Scroller.
  326.      */
  327.     public int getLineIncrement() {
  328.         return lineIncrement;
  329.     }
  330.  
  331.     /**
  332.      * Sets the page increment for this Scroller. This is the value
  333.      * that will be added (subtracted) when the user hits the page down
  334.      * (up) gadgets.
  335.      */
  336.     public void setPageIncrement(int l) {
  337.         if (l != pageIncrement) {
  338.             pageIncrement = l;
  339.             redoSliderRect();
  340.             redrawAsync();
  341.         }
  342.     }
  343.  
  344.     /**
  345.      * Gets the page increment for this Scroller.
  346.      */
  347.     public int getPageIncrement() {
  348.         return pageIncrement;
  349.     }
  350.  
  351.     /**
  352.      * Sets the unit value increment for the adjustable object.
  353.      * @param u the unit increment
  354.      */
  355.     public void setUnitIncrement(int u) {
  356.         if (u != lineIncrement) {
  357.             lineIncrement = u;
  358.             redoSliderRect();
  359.             redrawAsync();
  360.         }
  361.     }
  362.  
  363.     /**
  364.      * Gets the unit value increment for the adjustable object.
  365.      */
  366.     public int getUnitIncrement() {
  367.         return lineIncrement;
  368.     }
  369.  
  370.  
  371.     /**
  372.      * Sets the block value increment for the adjustable object.
  373.      * @param b the block increment
  374.      */
  375.     public void setBlockIncrement(int b) {
  376.         if (pageIncrement != b) {
  377.             pageIncrement = b;
  378.             redoSliderRect();
  379.             redrawAsync();
  380.         }
  381.     }
  382.  
  383.  
  384.     /**
  385.      * Gets the block value increment for the adjustable object.
  386.      */
  387.     public int getBlockIncrement() {
  388.         return pageIncrement;
  389.     }
  390.  
  391.     /**
  392.      * Sets the length of the proportionl indicator of the
  393.      * adjustable object.
  394.      * @param v the length of the indicator
  395.      */
  396.     public void setVisibleAmount(int v) {
  397.         int amount = Math.min(v, max-min+1);
  398.         if (thumbSize != amount) {
  399.             thumbSize = amount;
  400.             redoSliderRect();
  401.             redrawAsync();
  402.         }
  403.     }
  404.  
  405.     /**
  406.      * Gets the length of the propertional indicator.
  407.      */
  408.     public int getVisibleAmount() {
  409.         return thumbSize;
  410.     }
  411.  
  412.     /**
  413.      * Sets the minimum value of the adjustable object.
  414.      * @param min the minimum value
  415.      */
  416.      public void setMinimum(int min) {
  417.         boolean changed = false;
  418.         if (max < min) {
  419.             this.min = max;
  420.             max = min;
  421.             changed = true;
  422.         } else if (this.min != min) {
  423.             this.min = min;
  424.             changed = true;
  425.         }
  426.  
  427.         if (changed) {
  428.             setVisibleAmount(thumbSize);
  429.             redoSliderRect();
  430.             redrawAsync();
  431.         }
  432.      }
  433.  
  434.     /**
  435.      * Sets the maximum value of the adjustable object.
  436.      * @param max the maximum value
  437.      */
  438.      public void setMaximum(int max) {
  439.         boolean changed = false;
  440.         if (max < min) {
  441.             this.max = min;
  442.             min = max;
  443.             changed = true;
  444.         } else if (this.max != max) {
  445.             this.max = max;
  446.             changed = true;
  447.         }
  448.  
  449.         if (changed) {
  450.             setVisibleAmount(thumbSize);
  451.             redoSliderRect();
  452.             redrawAsync();
  453.         }
  454.      }
  455.  
  456.     /**
  457.      * Sets the values for this Scroller.
  458.      * @param value is the position in the current window.
  459.      * @param visible is the amount visible per page
  460.      * @param minimum is the minimum value of the Scroller
  461.      * @param maximum is the maximum value of the Scroller
  462.      */
  463.     public void setValues(int value, int visible, int minimum, int maximum) {
  464.         boolean changed = false;
  465.  
  466.         synchronized(LOCK) {
  467.  
  468.         if (maximum-minimum == 0) {
  469.             min = minimum;
  470.             max = min + 1;
  471.             thumbSize = 1;
  472.             value = min;
  473.             redrawAsync();
  474.  
  475.             return;
  476.         }
  477.  
  478.         if (maximum < minimum) {
  479.             int temp = maximum;
  480.             maximum = minimum;
  481.             minimum = temp;
  482.         }
  483.  
  484.         if (min != minimum || maximum != max) {
  485.             min = minimum;
  486.             max = maximum;
  487.             changed = true;
  488.         }
  489.  
  490.         if (thumbSize != visible || thumbSize > max-min+1) {
  491.             thumbSize = Math.max(Math.min(visible, max-min+1), 1);
  492.             changed = true;
  493.         }
  494.  
  495.         if (currentPos != value) {
  496.             currentPos = value;
  497.             changed = true;
  498.         }
  499.  
  500.         if (value < min) {
  501.             changed = true;
  502.             currentPos = min;
  503.         }
  504.  
  505.         if (value > max-thumbSize+1) {
  506.             changed = true;
  507.             currentPos = max-thumbSize+1;
  508.         }
  509.  
  510.         if (changed) {
  511.             redoSliderRect();
  512.             redrawAsync();
  513.         }
  514.         } //end synchronized
  515.     }
  516.  
  517.     /**
  518.      * Derived from java.awt.Component.
  519.      */
  520.     public void update(Graphics g) {
  521.         paint(g);
  522.     }
  523.  
  524.     /**
  525.      * Derived from java.awt.Component.
  526.      */
  527.     public void print(Graphics g) {
  528.         if (!isVisible() || lastSize == null) {
  529.             return;
  530.         }
  531.  
  532.         Graphics realG = imageG;
  533.         imageG = g;
  534.         try {
  535.             computeSliderRect();
  536.             drawScroller();
  537.         } catch(ArithmeticException ex) {}
  538.            imageG = realG;
  539.     }
  540.  
  541.     /**
  542.      * Derived from java.awt.Component.
  543.      */
  544.     public void paint(Graphics g) {
  545.         if (g instanceof PrintGraphics) {
  546.             print(g);
  547.             return;
  548.         }
  549.         if (image == null) {
  550.             redrawAsync();
  551.             return;
  552.         }
  553.  
  554.         synchronized (LOCK) {
  555.             g.drawImage(image, 0, 0, this);
  556.         }
  557.     }
  558.  
  559.     /**
  560.      * Reshapes the Component to the specified bounding box.
  561.      * @param x the x coordinate
  562.      * @param y the y coordinate
  563.      * @param width the width of the component
  564.      * @param height the height of the component
  565.      * @see #getBounds
  566.      * @see #setLocation
  567.      * @see #setSize
  568.      */
  569.     public void setBounds(int x, int y, int width, int height) {
  570.         reshape(x, y, width, height);
  571.     }
  572.  
  573.     /**
  574.      * @deprecated As of JDK version 1.1,
  575.      * replaced by setBounds(int, int, int, int).
  576.      */
  577.     public void reshape(int x, int y, int w, int h) {
  578.         //hack here - AWT deprecated functions are used as the implementation for the new
  579.         //functions - so must continue to use super.reshape()!!!!!
  580.         super.reshape(x, y, w, h);
  581.  
  582.         updateSliderRect = true;
  583.         redrawAsync();
  584.     }
  585.  
  586.     /**
  587.      * Derived from java.awt.Component.
  588.      */
  589.     public void setBounds(Rectangle r) {
  590.         super.setBounds(r);
  591.  
  592.         updateSliderRect = true;
  593.         redrawAsync();
  594.     }
  595.  
  596.     /**
  597.      * Derived from java.awt.Component.
  598.      */
  599.     public Dimension getPreferredSize() {
  600.         if (!isVisible()) {
  601.             return new Dimension(0, 0);
  602.         }
  603.  
  604.         if (orientation == HORIZONTAL) {
  605.             return new Dimension(scroll_ht*5, scroll_ht);
  606.         } else {
  607.             return new Dimension(scroll_ht, scroll_ht*5);
  608.         }
  609.     }
  610.  
  611.     /**
  612.      * @deprecated As of JDK version 1.1,
  613.      * replaced by getPreferredSize().
  614.      */
  615.     public Dimension preferredSize() {
  616.         return getPreferredSize();
  617.     }
  618.  
  619.     /**
  620.      * Derived from java.awt.Component.
  621.      */
  622.     public Dimension getMinimumSize() {
  623.         return preferredSize();
  624.     }
  625.  
  626.     /**
  627.      * @deprecated As of JDK version 1.1,
  628.      * replaced by getMinimumSize().
  629.      */
  630.     public Dimension minimumSize() {
  631.         return getMinimumSize();
  632.     }
  633.  
  634.     /**
  635.      * Creates an AdjustmentEvent with the specified ID with the scroller as the target and
  636.      * an Integer containing the current position as the argument.  The AdjustmentEvent
  637.      * instance is then fired using processEvent.
  638.      * @param id The ID of the event to be generated.
  639.      */
  640.     protected void generateEvent(int id) {
  641.         AdjustmentEvent e = new AdjustmentEvent(this,
  642.                                                 AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
  643.                                                 id,
  644.                                                 currentPos);
  645.         processEvent(e);
  646.     }
  647.  
  648.     /**
  649.      * Processes mouse events occurring on this component by
  650.      * dispatching them to any registered MouseListener objects.
  651.      * NOTE: This method will not be called unless mouse events
  652.      * are enabled for this component; this happens when one of the
  653.      * following occurs:
  654.      * a) A MouseListener object is registered via addMouseListener()
  655.      * b) Mouse events are enabled via enableEvents()
  656.      * Classes overriding this method should call super.processMouseEvent()
  657.      * to ensure default event processing continues normally.
  658.      * @param e the mouse event
  659.      */
  660.     protected void processMouseEvent(MouseEvent e) {
  661.         int id = e.getID();
  662.         switch(id) {
  663.           case MouseEvent.MOUSE_PRESSED:
  664.             mousePressed(e.getX(), e.getY());
  665.             break;
  666.           case MouseEvent.MOUSE_RELEASED:
  667.             mouseReleased(e.getX(), e.getY());
  668.             break;
  669.           case MouseEvent.MOUSE_CLICKED:
  670.             //no op
  671.             break;
  672.           case MouseEvent.MOUSE_EXITED:
  673.             mouseExited(e.getX(), e.getY());
  674.             break;
  675.           case MouseEvent.MOUSE_ENTERED:
  676.             mouseEntered(e.getX(), e.getY());
  677.             break;
  678.         }
  679.  
  680.         //let the parent class process the event as normal
  681.         super.processMouseEvent(e);
  682.     }
  683.  
  684.     /**
  685.      * Processes mouse motion events occurring on this component by
  686.      * dispatching them to any registered MouseMotionListener objects.
  687.      * NOTE: This method will not be called unless mouse motion events
  688.      * are enabled for this component; this happens when one of the
  689.      * following occurs:
  690.      * a) A MouseMotionListener object is registered via addMouseMotionListener()
  691.      * b) Mouse Motion events are enabled via enableEvents()
  692.      * Classes overriding this method should call super.processMouseMotionEvent()
  693.      * to ensure default event processing continues normally.
  694.      * @param e the mouse motion event
  695.      */
  696.     protected void processMouseMotionEvent(MouseEvent e) {
  697.         if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
  698.             mouseDragged(e.getX(), e.getY());
  699.         }
  700.  
  701.         super.processMouseMotionEvent(e);
  702.     }
  703.  
  704.     protected final void mousePressed(int x, int y) {
  705.         generateEvent(BEGIN_SCROLL);
  706.  
  707.         if (clickOnButton(x, y)) {
  708.             handleButtonClick(x, y);
  709.         } else if (clickOnSlider(x, y)) {
  710.             handleSliderDragStart(x, y);
  711.         } else {
  712.             handlePageClick(x, y);
  713.         }
  714.     }
  715.  
  716.     //variable to remember if the mouse button is depressed. This is needed mainly for
  717.     //the MAC where mouse capture does not work properly.
  718.     boolean down;
  719.  
  720.     /**
  721.      * Called by processMouseMovement when mouse has entered scroller.
  722.      */
  723.     protected final void mouseDragged(int x, int y) {
  724.         if (buttonDown()) {
  725.             handleButtonDrag(x, y);
  726.         } else if (sliderMoving()) {
  727.             handleSliderDrag(x, y);
  728.         } else {
  729.             handlePageDrag(x, y);
  730.         }
  731.  
  732.         down = true;
  733.     }
  734.  
  735.     /**
  736.      * Called by processMouseEvent when mouse has entered scroller.
  737.      */
  738.     protected final void mouseReleased(int x, int y) {
  739.         if (buttonDown()) {
  740.             handleButtonUp();
  741.         } else if (sliderMoving()) {
  742.             handleSliderUp();
  743.             updateSliderRect = true;
  744.             computeSliderRect();
  745.         } else {
  746.             handlePageUp();
  747.         }
  748.  
  749.         generateEvent(END_SCROLL);
  750.         down = false;
  751.     }
  752.  
  753.     /**
  754.      * Called by processMouseEvent when mouse has exited scroller.
  755.      */
  756.     protected final void mouseExited(int x, int y) {
  757.         if (down) {
  758.             mouseDragged(x, y);
  759.         }
  760.     }
  761.  
  762.     /**
  763.      * Derived from java.awt.Component.
  764.      */
  765.     public void mouseEntered(int x, int y) {
  766.         if (down) {
  767.             mouseDragged(x, y);
  768.         }
  769.     }
  770.  
  771.     /**
  772.      * Causes the scrollbar to be redrawn using an asynchronous thread.
  773.      */
  774.     public void redrawAsync() {
  775.         AsyncNotifier.notify(this);
  776.     }
  777.  
  778.     /**
  779.      * Callback function implemented from symantec.itools.db.awt.Notifiable.
  780.      */
  781.     public void asyncNotify(Vector args) {
  782.         redraw();
  783.     }
  784.  
  785.     Dimension   lastSize;
  786.     /**
  787.      * Synchronously draws the scrollbar.
  788.      */
  789.     private void redraw() {
  790.         synchronized (LOCK) {
  791.             //check for size change
  792.             Dimension size = getSize();
  793.  
  794.             if (size.height <= 0 || size.width <= 0) { return; }
  795.  
  796.             if (lastSize == null || lastSize.width != size.width
  797.                 || lastSize.height != size.height)
  798.             {
  799.                 image = createImage(size.width, size.height);
  800.                 /*<vj>*/
  801.                 if (image == null) {
  802.                     return;
  803.                 }
  804.                 /*<vj>*/
  805.                 imageG = image.getGraphics();
  806.                 if (imageG == null) {
  807.                     lastSize = null;
  808.                     return;
  809.                 }
  810.  
  811.                 lastSize = size;
  812.             }
  813.  
  814.             //draw scrollbar
  815.             try {
  816.                 computeSliderRect();
  817.                 drawScroller();
  818.             } catch(ArithmeticException ex) { }
  819.  
  820.             paint(getGraphics());
  821.         }
  822.     }
  823.  
  824.     /**
  825.      * Gets a reference to the actual Graphics object used to draw the scrollbar.
  826.      */
  827.     protected Graphics getImageGraphics() { return imageG; }
  828.  
  829.     /**
  830.      * Called to request the slider be drawn on the scrollbar.  This method may be
  831.      * overriden to give the slider a different look and feel from the default
  832.      * of a solid square.
  833.      * @param x The top left x coordinate.
  834.      * @param y The top left y coordinate.
  835.      * @param w The width of the slider in pixels.
  836.      * @param h The height of the slider in pixels.
  837.      * @param fg The foreground color to be used to draw the face of the slider.
  838.      * @param button Should always be SLIDER.
  839.      */
  840.     protected void drawSliderButton(int x, int y, int w, int h, Color fg, int button) {
  841.         drawButton(x, y, w, h, fg, button);
  842.     }
  843.  
  844.     /**
  845.      * Called by Scroller whenever a push button needs to be drawn. In its
  846.      * default configuration, the Scroller draws the scroll line up and down
  847.      * buttons and the slider using this method to draw the basic shape and
  848.      * shading.  The arrows on the up and down buttons are drawn following
  849.      * this method in the drawScrollButton() method.
  850.      * @param x The top left x coordinate.
  851.      * @param y The top left y coordinate.
  852.      * @param w The width of the slider in pixels.
  853.      * @param h The height of the slider in pixels.
  854.      * @param fg The foreground color to be used to draw the face of the slider.
  855.      * @param button One of UP, DOWN, LEFT, RIGHT, SLIDER.
  856.      */
  857.     protected void drawButton(int x, int y, int w, int h, Color fg, int button) {
  858.         imageG.setColor(fg);
  859.         imageG.fillRect(x+1, y+1, w-3, h-3);
  860.  
  861.         //draw highlight
  862.         boolean drawDown = button==buttonPushed && buttonDown;
  863.  
  864.         imageG.setColor(!drawDown ?upHl  :downHl);
  865.         imageG.drawLine(x+1, y+1, x+1, y+h-2); //left
  866.         imageG.drawLine(x+1, y+1, x+w-2, y+1);  //top
  867.         imageG.setColor(drawDown ?upHl  :downHl);
  868.         imageG.drawLine(x+w-2, y+1, x+w-2, y+h-2); //right
  869.         imageG.drawLine(x+1, y+h-2, x+w-2, y+h-2); //bottom
  870.  
  871.         //draw boundary
  872.         imageG.setColor(shapeColor);
  873.         imageG.drawRect(x, y, w-1, h-1);
  874.     }
  875.  
  876.     /**
  877.      * Called whenever a scroll line up/down button needs to be drawn.  If calls
  878.      * drawButton() then draws the arrow.
  879.      * @param x The top left x coordinate.
  880.      * @param y The top left y coordinate.
  881.      * @param w The width of the slider in pixels.
  882.      * @param h The height of the slider in pixels.
  883.      * @param direction One of LEFT, RIGHT, UP, or DOWN.
  884.      */
  885.     protected void drawScrollButton(int x, int y, int w, int h, int direction)
  886.     {
  887.         drawButton(x, y, w, h, fg, direction);
  888.  
  889.         Polygon poly = new Polygon();
  890.         switch (direction) {
  891.             case LEFT:
  892.                 if (w < 13) { return; }
  893.                 poly.addPoint(5, h/2);
  894.                 poly.addPoint(10, h/2+5);
  895.                 poly.addPoint(10, h/2-5);
  896.                 poly.addPoint(5, h/2);
  897.                 break;
  898.             case RIGHT:
  899.                 if (w < 12) { return; }
  900.                 poly.addPoint(x+w-5, h/2);
  901.                 poly.addPoint(x+w-10, h/2+5);
  902.                 poly.addPoint(x+w-10, h/2-5);
  903.                 poly.addPoint(x+w-5, h/2);
  904.                 break;
  905.             case UP:
  906.                 if (h < 13) { return; }
  907.                 poly.addPoint(w/2, 5);
  908.                 poly.addPoint(w/2+5, 10);
  909.                 poly.addPoint(w/2-5, 10);
  910.                 poly.addPoint(w/2, 5);
  911.                 break;
  912.             case DOWN:
  913.                 if (h < 12) { return; }
  914.                 poly.addPoint(w/2, y+h-5);
  915.                 poly.addPoint(w/2+5, y+h-10);
  916.                 poly.addPoint(w/2-5, y+h-10);
  917.                 poly.addPoint(w/2, y+h-5);
  918.         }
  919.  
  920.         imageG.setColor(shapeColor);
  921.         imageG.fillPolygon(poly);
  922.     }
  923.  
  924.     /**
  925.      * Called by the Scroller whenever the scrollbar needs to be drawn.  The
  926.      * drawing graphics context is guaranteed to be valid whenever this method
  927.      * is called.  It draws the scroll buttons, the slider, and fills in the
  928.      * background.
  929.      */
  930.     protected void drawScroller() {
  931.         Dimension size = lastSize;
  932.  
  933.         if (orientation == HORIZONTAL) {
  934.             int width = scroll_ht;
  935.             if (size.width < width*2) {
  936.                 width = size.width/2;
  937.             }
  938.  
  939.             //draw left scroll button
  940.             drawScrollButton(0,
  941.                              0,
  942.                              width,
  943.                              size.height,
  944.                              LEFT);
  945.  
  946.             //draw right scroll button
  947.             drawScrollButton(size.width-width,
  948.                              0,
  949.                              width,
  950.                              size.height,
  951.                              RIGHT);
  952.  
  953.             //draw slider
  954.             Rectangle sr = effectiveSlider();
  955.  
  956.  
  957.             drawSliderButton(sr.x, sr.y, sr.width, sr.height, fg, SLIDER);
  958.  
  959.             //erase everything else
  960.             imageG.setColor(bg);
  961.             //erase from left button to scroll button
  962.             if (width >= scroll_ht && sr.x > 0) {
  963.                 imageG.fillRect(width, 1, sr.x-width, size.height-2);
  964.             } else {
  965.                 imageG.fillRect(width, 1, size.width - width*2, size.height-2);
  966.             }
  967.  
  968.             //erase from right side of scroll button to right button
  969.             if (width >= scroll_ht && sr.x > 0) {
  970.                 imageG.fillRect(sr.x+sr.width, 1, size.width-(sr.x+sr.width)-width, size.height-2);
  971.             }
  972.         } else {
  973.             //draw vertical Scroller
  974.             int height = scroll_ht;
  975.             if (size.height < height*2) {
  976.                 height = size.height/2;
  977.             }
  978.  
  979.             //draw top scroll button
  980.             drawScrollButton(0,
  981.                              0,
  982.                              size.width,
  983.                              height,
  984.                              UP);
  985.  
  986.             //draw bottom scroll button
  987.             drawScrollButton(0,
  988.                              size.height - height,
  989.                              size.width,
  990.                              height,
  991.                              DOWN);
  992.  
  993.             //draw slider
  994.             Rectangle sr = effectiveSlider();
  995.             drawButton(sr.x, sr.y, sr.width, sr.height, fg, SLIDER);
  996.  
  997.             //erase everything else
  998.             imageG.setColor(bg);
  999.             //erase from left button to scroll button
  1000.             if (height >= scroll_ht && sr.y > 0) {
  1001.                 imageG.fillRect(1, height, size.width-2, sr.y-height);
  1002.             } else {
  1003.                 imageG.fillRect(1, height, size.width-2, size.height - height*2);
  1004.             }
  1005.  
  1006.             //erase from right side of scroll button to right button
  1007.             if (height >= scroll_ht && sr.y > 0) {
  1008.                 imageG.fillRect(1, sr.y+sr.height, size.width-2, size.height-(sr.y+sr.height)-height);
  1009.             }
  1010.         }
  1011.  
  1012.         //draw boundary
  1013.         imageG.setColor(borderColor);
  1014.         imageG.drawRect(0,
  1015.                         0,
  1016.                         size.width-1,
  1017.                         size.height-1);
  1018.     }
  1019.  
  1020.     /**
  1021.      * Computes the position and dimensions of the slider based on the current
  1022.      * values.
  1023.      */
  1024.     Rectangle effectiveSlider() {
  1025.         if (orientation == HORIZONTAL) {
  1026.             if (lastSize.width < scroll_ht*2 + 7) {
  1027.                 return new Rectangle(-100, -100, 1, 1);
  1028.             }
  1029.  
  1030.             return new Rectangle(Math.min(lastSize.width-zeroPt-(int)effThumbSize+1,
  1031.                                           Math.max(zeroPt, sliderRect.x)),
  1032.                                  sliderRect.y,
  1033.                                  sliderRect.width,
  1034.                                  sliderRect.height);
  1035.         } else {
  1036.             if (lastSize.height < scroll_ht*2 + 7) {
  1037.                 return new Rectangle(-100, -100, 1, 1);
  1038.             }
  1039.  
  1040.             return new Rectangle(sliderRect.x,
  1041.                                  Math.min(lastSize.height-zeroPt-(int)effThumbSize+1,
  1042.                                           Math.max(zeroPt, sliderRect.y)),
  1043.                                  sliderRect.width,
  1044.                                  sliderRect.height);
  1045.         }
  1046.     }
  1047.  
  1048.     /**
  1049.      * Determines if the specified coordinates are a button push of some type.
  1050.      */
  1051.     boolean clickOnButton(int x, int y) {
  1052.         Dimension size = lastSize;
  1053.  
  1054.         if (orientation == HORIZONTAL
  1055.             && (x < scroll_ht || x > size.width - scroll_ht))
  1056.         {
  1057.             return true;
  1058.         } else if (orientation == VERTICAL
  1059.             && (y < scroll_ht || y > size.height - scroll_ht))
  1060.         {
  1061.             return true;
  1062.         }
  1063.  
  1064.         return false;
  1065.     }
  1066.  
  1067.     /**
  1068.      * Perfroms the necessary event gernation and redrawing required when the
  1069.      * user clicks on a button.  It starts the timer up to generate a callback
  1070.      * after 450 msecs.  It is later reduced to 50msecs, but the initial time
  1071.      * permits the user to daddle for a little with the button down without
  1072.      * getting fast scrolling.
  1073.      */
  1074.     void handleButtonClick(int x, int y) {
  1075.         Dimension size = lastSize;
  1076.  
  1077.         if (orientation == HORIZONTAL) {
  1078.             int width = scroll_ht;
  1079.             if (size.width < width*2) {
  1080.                 width = size.width/2;
  1081.             }
  1082.  
  1083.             if (x < width) {
  1084.                 int orig = currentPos;
  1085.                 currentPos = Math.max(currentPos-lineIncrement, min);
  1086.                 if (orig != currentPos) {
  1087.                     generateEvent(AdjustmentEvent.UNIT_DECREMENT);
  1088.                 }
  1089.  
  1090.                 buttonPushed = LEFT;
  1091.                 buttonDown = true;
  1092.             } else if (x > size.width - width) {
  1093.                 int orig = currentPos;
  1094.                 currentPos = Math.min(currentPos+lineIncrement, max-thumbSize+1);
  1095.                 if (orig != currentPos) {
  1096.                     generateEvent(AdjustmentEvent.UNIT_INCREMENT);
  1097.                 }
  1098.                 buttonPushed = RIGHT;
  1099.                 buttonDown = true;
  1100.             }
  1101.         } else if (orientation == VERTICAL) {
  1102.             int height = scroll_ht;
  1103.             if (size.height < height*2) {
  1104.                 height = size.height/2;
  1105.             }
  1106.  
  1107.             if (y < height) {
  1108.                 int orig = currentPos;
  1109.                 currentPos = Math.max(currentPos-lineIncrement, min);
  1110.                 if (orig != currentPos) {
  1111.                     generateEvent(AdjustmentEvent.UNIT_DECREMENT);
  1112.                 }
  1113.                 buttonPushed = UP;
  1114.                 buttonDown = true;
  1115.             } else if (y > size.height - height) {
  1116.                 int orig = currentPos;
  1117.                 currentPos = Math.min(currentPos+lineIncrement, max-thumbSize+1);
  1118.                 if (orig != currentPos) {
  1119.                     generateEvent(AdjustmentEvent.UNIT_INCREMENT);
  1120.                 }
  1121.                 buttonPushed = DOWN;
  1122.                 buttonDown = true;
  1123.             }
  1124.         }
  1125.  
  1126.         redoSliderRect();
  1127.         redrawAsync();
  1128.         timer.activate(this, 450);
  1129.     }
  1130.  
  1131.     /**
  1132.      * Returns whether a scroll button is currently pressed.
  1133.      */
  1134.     protected boolean buttonDown() {
  1135.         return buttonPushed >= LEFT && buttonPushed <= DOWN;
  1136.     }
  1137.  
  1138.     /**
  1139.      * Called when a scroll button is pushed by the user and the mouse is
  1140.      * dragged from its last position.  If the user moves the mouse off of
  1141.      * the button area, the button is raised and timer events are ceased.
  1142.      * When the button is later depressed, timer events are resumed.
  1143.      */
  1144.     void handleButtonDrag(int x, int y) {
  1145.         Dimension size = lastSize;
  1146.         boolean before = buttonDown;
  1147.  
  1148.         switch (buttonPushed) {
  1149.             case LEFT:
  1150.                 buttonDown = (x<scroll_ht && x>0 && y>0 && y<size.height);
  1151.                 break;
  1152.             case RIGHT:
  1153.                 buttonDown = (x<size.width && x>size.width - scroll_ht
  1154.                               &&y>0 && y<size.height);
  1155.                 break;
  1156.             case UP:
  1157.                 buttonDown = (y>0 && y<scroll_ht && x>0 && x<size.width);
  1158.                 break;
  1159.             case DOWN:
  1160.                 buttonDown = (y > size.height - scroll_ht && y<size.height
  1161.                              && x>0 && x<size.width);
  1162.                 break;
  1163.             default:
  1164.                 return;
  1165.         }
  1166.  
  1167.         if (buttonDown != before) {
  1168.             if (buttonDown) {
  1169.                 timer.activate(this);
  1170.             } else {
  1171.                 timer.inactivate(this);
  1172.             }
  1173.  
  1174.             redrawAsync();
  1175.         }
  1176.     }
  1177.  
  1178.     /**
  1179.      * Helper method called when the mouse button is released after a scroll
  1180.      * button is released.  Redraws the scrollbar and stops timer events.
  1181.      */
  1182.     void handleButtonUp() {
  1183.         buttonPushed = NONE;
  1184.         redrawAsync();
  1185.         timer.inactivate(this);
  1186.     }
  1187.  
  1188.     /**
  1189.      * The current position and dimensions of the slider.
  1190.      */
  1191.     Rectangle   sliderRect;
  1192.     /**
  1193.      * The end of the up scroll button.
  1194.      */
  1195.     int         zeroPt;
  1196.     /**
  1197.      * The number of pixels the long/wide the slider is to be drawn.
  1198.      */
  1199.     float       effThumbSize;
  1200.     boolean     updateSliderRect;
  1201.     /**
  1202.      * Caused the position and dimensions of the slider to be recalculated.
  1203.      */
  1204.     protected void redoSliderRect() {
  1205.         updateSliderRect = true;
  1206.         computeSliderRect();
  1207.     }
  1208.  
  1209.     /**
  1210.      * Determines the size and position of the slider rect if it has not already
  1211.      * be calculated.
  1212.      */
  1213.     void computeSliderRect() {
  1214.         if ((sliderRect != null && !updateSliderRect)
  1215.             || lastSize == null)
  1216.         {
  1217.             return;
  1218.         }
  1219.  
  1220.         if (max-min == 0) {
  1221.             sliderRect = new Rectangle(0, 0, 0, 0);
  1222.             return;
  1223.         }
  1224.  
  1225.         updateSliderRect = false;
  1226.         Dimension size = lastSize;
  1227.  
  1228.         zeroPt = scroll_ht;
  1229.         if (orientation == HORIZONTAL) {
  1230.             if (size.width < zeroPt*2) {
  1231.                 zeroPt = size.width/2;
  1232.             }
  1233.  
  1234.             int effWidth = size.width-zeroPt*2;
  1235.             effThumbSize = (thumbSize*effWidth)/(max-min+1);
  1236.             effThumbSize = Math.max(effThumbSize, 7);
  1237.  
  1238.             sliderRect = new Rectangle(
  1239.                               (int)(zeroPt + effWidth * (currentPos-min)/(max-min+1)),
  1240.                               0,
  1241.                               (int)effThumbSize+1,
  1242.                               size.height);
  1243.         } else {
  1244.             if (size.height < zeroPt*2) {
  1245.                 zeroPt = size.height/2;
  1246.             }
  1247.  
  1248.             int effHeight = size.height-zeroPt*2;
  1249.             effThumbSize = (thumbSize*effHeight)/(max-min+1);
  1250.             effThumbSize = Math.max(effThumbSize, 7);
  1251.  
  1252.             sliderRect = new Rectangle(0,
  1253.                              (int)(zeroPt + effHeight * (currentPos-min)/(max-min+1)),
  1254.                              size.width,
  1255.                              (int)effThumbSize+1);
  1256.         }
  1257.     }
  1258.  
  1259.     /**
  1260.      * Helper method called to determine if a mouse down occured on the slider.
  1261.      */
  1262.     boolean clickOnSlider(int x, int y) {
  1263.         computeSliderRect();
  1264.         return sliderRect.contains(x, y);
  1265.     }
  1266.  
  1267.     /**
  1268.      * Relative start x coordinate for absolute scrolling using the slider. Distance is
  1269.      * measured relative to the top/front of the slider.
  1270.      */
  1271.     int startDragX;
  1272.     /**
  1273.      * Relative start y coordinate for absolute scrolling using the slider. Distance is
  1274.      * measured relative to the top/front of the slider.
  1275.      */
  1276.     int startDragY;
  1277.  
  1278.     /**
  1279.      * Helper method called to handle when the user starts absolute scrolling.
  1280.      */
  1281.     void handleSliderDragStart(int x, int y) {
  1282.         startDragX = x - sliderRect.x;
  1283.         startDragY = y - sliderRect.y;
  1284.  
  1285.         buttonPushed = SLIDER;
  1286.         buttonDown = true;
  1287.  
  1288.         redrawAsync();
  1289.     }
  1290.  
  1291.     /**
  1292.      * Gets whether the slider is being absolutely scrolled.
  1293.      */
  1294.     protected boolean sliderMoving() {
  1295.         return buttonPushed == SLIDER;
  1296.     }
  1297.  
  1298.     /**
  1299.      * Helper method for when absolute scrolling is in progress and the user moves
  1300.      * the mouse.
  1301.      */
  1302.     void handleSliderDrag(int x, int y) {
  1303.         Dimension size = lastSize;
  1304.         int origPos = currentPos;
  1305.  
  1306.         //could possibly be null so get it
  1307.         computeSliderRect();
  1308.         if (orientation == HORIZONTAL) {
  1309.             int delta = x - sliderRect.x - startDragX;
  1310.  
  1311.             sliderRect.x += delta;
  1312.             currentPos = computeValue(sliderRect.x-zeroPt);
  1313.         } else {
  1314.             int delta = y - sliderRect.y - startDragY;
  1315.  
  1316.             sliderRect.y += delta;
  1317.             currentPos = computeValue(sliderRect.y-zeroPt);
  1318.         }
  1319.  
  1320.         if (origPos != currentPos) {
  1321.             generateEvent(AdjustmentEvent.TRACK);
  1322.         }
  1323.  
  1324.         redrawAsync();
  1325.     }
  1326.  
  1327.     /**
  1328.      * Helper method used to determine the value represented by the current
  1329.      * position of the slider.
  1330.      * @param val slider positon minus the width of the scroll up button.
  1331.      */
  1332.     int computeValue(int val) {
  1333.         float  pos;
  1334.  
  1335.         if (orientation == HORIZONTAL) {
  1336.             pos = ((float)val)/(lastSize.width-zeroPt*2)*(max-min+1)+min;
  1337.         } else {
  1338.             pos = ((float)val)/(lastSize.height-zeroPt*2)*(max-min+1)+min;
  1339.         }
  1340.  
  1341.         return Math.min(max-thumbSize+1, Math.max(min, (int)pos));
  1342.     }
  1343.  
  1344.     /**
  1345.      * Helper method called when the slider is released following absolute scrolling.
  1346.      */
  1347.     void handleSliderUp() {
  1348.         buttonPushed = NONE;
  1349.         buttonDown = false;
  1350.  
  1351.         redrawAsync();
  1352.     }
  1353.  
  1354.     /**
  1355.      * The direction of paging (PAGING_UP, PAGING_DOWN, or SUSPENDED)
  1356.      */
  1357.     int pageDirection;
  1358.     /**
  1359.      * The coordinates the user last caused a mouse event during paging.
  1360.      */
  1361.     Point startPagingPt;
  1362.     /**
  1363.      * Paging up.
  1364.      */
  1365.     static final int PAGING_UP = 0;
  1366.     /**
  1367.      * Paging down.
  1368.      */
  1369.     static final int PAGING_DOWN = 1;
  1370.     /**
  1371.      * Paging suspended because the slider is currently positioned where the mouse
  1372.      * is located.
  1373.      */
  1374.     static final int SUSPENDED = 2;
  1375.  
  1376.     /**
  1377.      * Helper method called to start paging for the user. Generates the required
  1378.      * events and triggers the timer to start sending events.
  1379.      */
  1380.     void handlePageClick(int x, int y) {
  1381.         startPagingPt = new Point(x, y);
  1382.  
  1383.         if (orientation == HORIZONTAL) {
  1384.             if (x < sliderRect.x) {
  1385.                 pageDirection = PAGING_UP;
  1386.                 currentPos = Math.max(min, currentPos-pageIncrement);
  1387.                 generateEvent(AdjustmentEvent.BLOCK_DECREMENT);
  1388.             } else {
  1389.                 pageDirection = PAGING_DOWN;
  1390.                 currentPos = Math.min(max-thumbSize+1, currentPos+pageIncrement);
  1391.                 generateEvent(AdjustmentEvent.BLOCK_INCREMENT);
  1392.             }
  1393.         } else {
  1394.             if (y < sliderRect.y) {
  1395.                 pageDirection = PAGING_UP;
  1396.                 currentPos = Math.max(min, currentPos-pageIncrement);
  1397.                 generateEvent(AdjustmentEvent.BLOCK_DECREMENT);
  1398.             } else {
  1399.                 pageDirection = PAGING_DOWN;
  1400.                 currentPos = Math.min(max-thumbSize+1, currentPos+pageIncrement);
  1401.                 generateEvent(AdjustmentEvent.BLOCK_INCREMENT);
  1402.             }
  1403.         }
  1404.  
  1405.         redoSliderRect();
  1406.         redrawAsync();
  1407.         timer.activate(this, 450);
  1408.     }
  1409.  
  1410.     /**
  1411.      * Helper method called to handle a mouse drag during paging. If the mouse is
  1412.      * dragged to a position where the slider is located, paging is suspended. If
  1413.      * it is moved in a position such that the slider must move in the same paging
  1414.      * direction as initially indicated by the user, paging is continued/resumed.
  1415.      */
  1416.     void handlePageDrag(int x, int y) {
  1417.         startPagingPt = new Point(x, y);
  1418.         if (pageDirection >= SUSPENDED) {
  1419.             pageDirection -= SUSPENDED;
  1420.             timer.activate(this, 50);
  1421.         }
  1422.     }
  1423.  
  1424.     /**
  1425.      * Helper method called when the user releases the mouse following paging.
  1426.      */
  1427.     void handlePageUp() {
  1428.         timer.inactivate(this);
  1429.     }
  1430.  
  1431.     /**
  1432.      * Method implemented from the Observer interface.  Used by the timer.
  1433.      */
  1434.     public void update(Observable obs, Object o) {
  1435.         timer.updateInterval(this, 50);
  1436.  
  1437.         int orig = currentPos;
  1438.         switch (buttonPushed) {
  1439.             case DOWN:
  1440.                 currentPos = Math.min(max-thumbSize+1, currentPos+lineIncrement);
  1441.                 if (orig != currentPos) {
  1442.                     generateEvent(AdjustmentEvent.UNIT_INCREMENT);
  1443.                 }
  1444.                 break;
  1445.             case UP:
  1446.                 currentPos = Math.max(min, currentPos-lineIncrement);
  1447.                 if (orig != currentPos) {
  1448.                     generateEvent(AdjustmentEvent.UNIT_DECREMENT);
  1449.                 }
  1450.                 break;
  1451.             case LEFT:
  1452.                 currentPos = Math.max(min, currentPos-lineIncrement);
  1453.                 if (orig != currentPos) {
  1454.                     generateEvent(AdjustmentEvent.UNIT_DECREMENT);
  1455.                 }
  1456.                 break;
  1457.             case RIGHT:
  1458.                 currentPos = Math.min(max-thumbSize+1, currentPos+lineIncrement);
  1459.                 if (orig != currentPos) {
  1460.                     generateEvent(AdjustmentEvent.UNIT_INCREMENT);
  1461.                 }
  1462.                 break;
  1463.             default:
  1464.                 synchronized(LOCK) {
  1465.                 //must be a page request
  1466.                 if (pageDirection == PAGING_UP) {
  1467.                     if ((orientation == HORIZONTAL && startPagingPt.x >= sliderRect.x)
  1468.                         || (orientation == VERTICAL && startPagingPt.y >= sliderRect.y))
  1469.                     {
  1470.                         pageDirection += SUSPENDED;
  1471.                         timer.inactivate(this);
  1472.                     } else {
  1473.                         currentPos = Math.max(min, currentPos-pageIncrement);
  1474.                         generateEvent(AdjustmentEvent.BLOCK_DECREMENT);
  1475.                     }
  1476.                 } else if (pageDirection == PAGING_DOWN){
  1477.                     if ((orientation == HORIZONTAL && startPagingPt.x <= sliderRect.x+sliderRect.width)
  1478.                         || (orientation == VERTICAL && startPagingPt.y <= sliderRect.y+sliderRect.height))
  1479.                     {
  1480.                         pageDirection += SUSPENDED;
  1481.                         timer.inactivate(this);
  1482.                     } else {
  1483.                         currentPos = Math.min(max-thumbSize+1, currentPos+pageIncrement);
  1484.                         generateEvent(AdjustmentEvent.BLOCK_INCREMENT);
  1485.                     }
  1486.                 }
  1487.                 }
  1488.         }
  1489.  
  1490.         redoSliderRect();
  1491.         redrawAsync();
  1492.     }
  1493.  
  1494.     //---------------------------------------------------------------------------
  1495.     //          Adustable methods
  1496.     //---------------------------------------------------------------------------
  1497.  
  1498.     /**
  1499.      * Processes events on this scrollbar. If the event is an
  1500.      * AdjustmentEvent, it invokes the processAdjustmentEvent method,
  1501.      * else it invokes its superclass's processEvent.
  1502.      * @param e the event
  1503.      */
  1504.     protected void processEvent(AWTEvent e) {
  1505.         if (e instanceof AdjustmentEvent) {
  1506.             processAdjustmentEvent((AdjustmentEvent)e);
  1507.             return;
  1508.         }
  1509.  
  1510.         super.processEvent(e);
  1511.     }
  1512.  
  1513.     /**
  1514.      * Processes adjustment events occurring on this scrollbar by
  1515.      * dispatching them to any registered AdjustmentListener objects.
  1516.      * NOTE: This method will not be called unless adjustment events
  1517.      * are enabled for this component; this happens when one of the
  1518.      * following occurs:
  1519.      * a) An AdjustmentListener object is registered via addAdjustmentListener()
  1520.      * b) Adjustment events are enabled via enableEvents()
  1521.      * @see Component#enableEvents
  1522.      * @param e the adjustment event
  1523.      */
  1524.     protected void processAdjustmentEvent(AdjustmentEvent e) {
  1525.         if (adjustmentListener != null) {
  1526.             adjustmentListener.adjustmentValueChanged(e);
  1527.         }
  1528.     }
  1529.  
  1530.     /**
  1531.      * Adds the specified adjustment listener  to recieve adjustment events
  1532.      * from this scroller.
  1533.      * @param l the adjustment listener
  1534.      */
  1535.     public void addAdjustmentListener(AdjustmentListener l) {
  1536.         adjustmentListener = AWTEventMulticaster.add(adjustmentListener, l);
  1537.     }
  1538.  
  1539.     /**
  1540.      * Removes the specified adjustment listener so that it no longer
  1541.      * receives adjustment events from this scroller.
  1542.      */
  1543.     public void removeAdjustmentListener(AdjustmentListener l) {
  1544.         adjustmentListener = AWTEventMulticaster.remove(adjustmentListener, l);
  1545.     }
  1546.  
  1547. }