home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / VCAFE.3.0A / JFC.bin / BasicScrollBarUI.java < prev    next >
Text File  |  1998-06-30  |  32KB  |  981 lines

  1. /*
  2.  * @(#)BasicScrollBarUI.java    1.42 98/04/10
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.plaf.basic;
  21.  
  22.  
  23. import java.awt.Component;
  24. import java.awt.Container;
  25. import java.awt.LayoutManager;
  26. import java.awt.Adjustable;
  27. import java.awt.event.AdjustmentListener;
  28. import java.awt.event.AdjustmentEvent;
  29. import java.awt.event.ActionListener;
  30. import java.awt.event.ActionEvent;
  31. import java.awt.event.MouseMotionListener;
  32. import java.awt.event.MouseAdapter;
  33. import java.awt.event.MouseEvent;
  34. import java.awt.Graphics;
  35. import java.awt.Dimension;
  36. import java.awt.Rectangle;
  37. import java.awt.Point;
  38. import java.awt.Insets;
  39. import java.awt.Color;
  40. import java.io.Serializable;
  41. import java.awt.IllegalComponentStateException;
  42.  
  43. import com.sun.java.swing.*;
  44. import com.sun.java.swing.event.*;
  45. import com.sun.java.swing.plaf.*;
  46.  
  47.  
  48. /**
  49.  * Implementation of ScrollBarUI for the Basic Look and Feel
  50.  * <p>
  51.  * Warning: serialized objects of this class will not be compatible with
  52.  * future swing releases.  The current serialization support is appropriate 
  53.  * for short term storage or RMI between Swing1.0 applications.  It will
  54.  * not be possible to load serialized Swing1.0 objects with future releases
  55.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  56.  * baseline for the serialized form of Swing objects.
  57.  *
  58.  * @version 1.42 04/10/98
  59.  * @author David Kloba
  60.  * @author Hans Muller
  61.  */
  62. public class BasicScrollBarUI 
  63.     extends ScrollBarUI implements LayoutManager, Serializable, SwingConstants
  64. {
  65.     /* The following fields are returned by the corresponding
  66.      * get methods rather than heap allocating new Dimension 
  67.      * objects.
  68.      */
  69.     private static final Dimension minimumThumbSize = new Dimension(8, 8);
  70.     private static final Dimension maximumThumbSize = new Dimension(4096, 4096);
  71.  
  72.     /* The following static fields are lazily computed by 
  73.      * configureScrollBarColors() and are shared by all BasicScrollBarUI 
  74.      * instances.  BasicScrollBarUI subclasses should override
  75.      * configureScrollBarColors() and initialize whatever set
  76.      * of shared color fields are appropriate.
  77.      */
  78.  
  79.     private static Color thumbHighlightColor;
  80.     private static Color thumbLightShadowColor;
  81.     private static Color thumbDarkShadowColor;
  82.     private static Color thumbColor;
  83.     private static Color trackColor;
  84.     private static Color trackHighlightColor;
  85.     private static boolean scrollBarColorsInitialized = false;
  86.  
  87.     /* ScrollBar parts, listeners, and cached geometry.
  88.      */
  89.  
  90.     protected JScrollBar scrollbar;
  91.     protected JButton incrButton;
  92.     protected JButton decrButton;
  93.     protected boolean isDragging;
  94.     protected TrackListener trackListener;
  95.     protected ArrowButtonListener buttonListener;
  96.     protected ModelListener modelListener;
  97.  
  98.     private Rectangle thumbRect;
  99.     private Rectangle trackRect;
  100.  
  101.     protected int trackHighlight;
  102.     protected static final int NO_HIGHLIGHT = 0;
  103.     protected static final int DECREASE_HIGHLIGHT = 1;
  104.     protected static final int INCREASE_HIGHLIGHT = 2;
  105.  
  106.     protected ScrollListener scrollListener;
  107.     protected Timer scrollTimer;
  108.  
  109.     
  110.     /*
  111.      * ScrollBarUI Implementation
  112.      */
  113.  
  114.     public static ComponentUI createUI(JComponent c)    {
  115.         return new BasicScrollBarUI();
  116.     }
  117.  
  118.  
  119.     /**
  120.      * Initialize the private static colors used by <code>paintKnob()</code> 
  121.      * and <code>paintTrack()</code>.  Subclasses that override either of
  122.      * these methods should override this method as well, and set up
  123.      * any colors they need.
  124.      */
  125.     protected void configureScrollBarColors() 
  126.     {
  127.     if (!scrollBarColorsInitialized) {
  128.         thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
  129.         thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbLightShadow");
  130.         thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
  131.         thumbColor = UIManager.getColor("ScrollBar.thumb");
  132.         trackColor = UIManager.getColor("ScrollBar.track");
  133.         trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
  134.         scrollBarColorsInitialized = true;
  135.     }
  136.     }
  137.  
  138.  
  139.     public void installUI(JComponent c)   {
  140.     scrollbar = (JScrollBar)c;
  141.  
  142.         thumbRect = new Rectangle(0, 0, 0, 0);
  143.         trackRect = new Rectangle(0, 0, 0, 0);
  144.     trackHighlight = NO_HIGHLIGHT;
  145.  
  146.         switch (((JScrollBar)c).getOrientation()) {
  147.         case JScrollBar.VERTICAL:
  148.             incrButton = createIncreaseButton(SOUTH);
  149.             decrButton = createDecreaseButton(NORTH);
  150.             break;
  151.             
  152.         case JScrollBar.HORIZONTAL:
  153.             incrButton = createIncreaseButton(EAST);
  154.             decrButton = createDecreaseButton(WEST);
  155.             break;
  156.         }
  157.                 
  158.         scrollbar.setLayout(this);
  159.         scrollbar.add(incrButton);
  160.         scrollbar.add(decrButton);
  161.  
  162.         trackListener = new TrackListener();
  163.         buttonListener = new ArrowButtonListener();
  164.         modelListener = new ModelListener();
  165.  
  166.         scrollbar.addMouseListener(trackListener);
  167.         scrollbar.addMouseMotionListener(trackListener);
  168.         scrollbar.getModel().addChangeListener(modelListener);
  169.  
  170.         if (incrButton != null) {
  171.             incrButton.addMouseListener(buttonListener);
  172.     }
  173.         if (decrButton != null)    {
  174.             decrButton.addMouseListener(buttonListener);
  175.     }
  176.         
  177.     // PENDING - this is a crock
  178.     scrollbar.setEnabled(scrollbar.isEnabled());
  179.     scrollbar.setOpaque(true);
  180.  
  181.     scrollListener = new ScrollListener();
  182.     scrollTimer = new Timer(100, scrollListener);
  183.     scrollTimer.setInitialDelay(300);
  184.  
  185.         LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
  186.  
  187.     configureScrollBarColors();
  188.     }
  189.  
  190.  
  191.     public void uninstallUI(JComponent c) 
  192.     {
  193.     scrollTimer.stop();
  194.     scrollTimer = null;
  195.         
  196.         if (decrButton != null) {
  197.             decrButton.removeMouseListener(buttonListener);
  198.     }
  199.  
  200.         if (incrButton != null) {
  201.             incrButton.removeMouseListener(buttonListener);
  202.     }
  203.  
  204.         scrollbar.getModel().removeChangeListener(modelListener);
  205.         scrollbar.removeMouseListener(trackListener);
  206.         scrollbar.removeMouseMotionListener(trackListener);
  207.  
  208.         scrollbar.remove(incrButton);
  209.         scrollbar.remove(decrButton);
  210.         scrollbar.setLayout(null);
  211.  
  212.     thumbRect = null;
  213.     incrButton = null;
  214.     decrButton = null;
  215.     scrollbar = null;
  216.     }
  217.     
  218.  
  219.     public void paint(Graphics g, JComponent c) {
  220.     paintTrack(g, c, getTrackBounds());        
  221.     paintThumb(g, c, getThumbBounds());
  222.     }
  223.  
  224.         
  225.     /**
  226.      * A vertical scrollbars preferred width is the maximum of 
  227.      * preferred widths of the (non null) increment/decrement buttons,
  228.      * and the minimum width of the thumb. The preferred height is the 
  229.      * sum of the preferred heights of the same parts.  The basis for 
  230.      * the preferred size of a horizontal scrollbar is similar. 
  231.      * <p>
  232.      * The preferredSize is only computed once, subequent
  233.      * calls to this method just return a cached size.  T
  234.      * 
  235.      * @param c The JScrollBar that's delegating this method to us.
  236.      * @return The preferred size of a Basic JScrollBar.
  237.      * @see #getMaximumSize
  238.      * @see #getMinimumSize
  239.      */
  240.     public Dimension getPreferredSize(JComponent c) {
  241.     return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
  242.         ? new Dimension(16, 48)
  243.         : new Dimension(48, 16);
  244.     }
  245.  
  246.  
  247.     /**
  248.      * A vertical scrollbars minimum width is the largest 
  249.      * minimum width of the (non null) increment/decrement buttons,
  250.      * and the minimum width of the thumb. The minimum height is the 
  251.      * sum of the minimum heights of the same parts.  The basis for 
  252.      * the preferred size of a horizontal scrollbar is similar. 
  253.      * <p>
  254.      * The minimumSize is only computed once, subequent
  255.      * calls to this method just return a cached size.  T
  256.      * 
  257.      * @param c The JScrollBar that's delegating this method to us.
  258.      * @return The minimum size of a Basic JScrollBar.
  259.      * @see #getMaximumSize
  260.      * @see #getPreferredSize
  261.      */
  262.     public Dimension getMinimumSize(JComponent c) {
  263.     return getPreferredSize(c);
  264.     }
  265.  
  266.     
  267.     /**
  268.      * @param c The JScrollBar that's delegating this method to us.
  269.      * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  270.      * @see #getMinimumSize
  271.      * @see #getPreferredSize
  272.      */
  273.     public Dimension getMaximumSize(JComponent c) {
  274.         return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  275.     }
  276.         
  277.     
  278.     protected JButton createDecreaseButton(int orientation)  {
  279.         return new BasicArrowButton(orientation);
  280.     }
  281.  
  282.     protected JButton createIncreaseButton(int orientation)  {
  283.         return new BasicArrowButton(orientation);
  284.     }
  285.                     
  286.  
  287.     protected void paintDecreaseHighlight(Graphics g)
  288.     {
  289.     Insets insets = scrollbar.getInsets();
  290.     Rectangle thumbR = getThumbBounds();
  291.     g.setColor(trackHighlightColor);
  292.  
  293.     if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  294.         int x = insets.left;
  295.         int y = decrButton.getY() + decrButton.getHeight();
  296.         int w = scrollbar.getWidth() - (insets.left + insets.right);
  297.         int h = thumbR.y - y;
  298.         g.fillRect(x, y, w, h);
  299.     } 
  300.     else    {
  301.         int x = decrButton.getX() + decrButton.getHeight();
  302.         int y = insets.top;
  303.         int w = thumbR.x - x;
  304.         int h = scrollbar.getHeight() - (insets.top + insets.bottom);
  305.         g.fillRect(x, y, w, h);
  306.     }
  307.     }      
  308.     
  309.  
  310.     protected void paintIncreaseHighlight(Graphics g)
  311.     {
  312.     Insets insets = scrollbar.getInsets();
  313.     Rectangle thumbR = getThumbBounds();
  314.     g.setColor(trackHighlightColor);
  315.  
  316.     if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  317.         int x = insets.left;
  318.         int y = thumbR.y + thumbR.height;
  319.         int w = scrollbar.getWidth() - (insets.left + insets.right);
  320.         int h = incrButton.getY() - y;
  321.         g.fillRect(x, y, w, h);
  322.     } 
  323.     else {
  324.         int x = thumbR.x + thumbR.width;
  325.         int y = insets.top;
  326.         int w = incrButton.getX() - x;
  327.         int h = scrollbar.getHeight() - (insets.top + insets.bottom);
  328.         g.fillRect(x, y, w, h);
  329.     }
  330.     }      
  331.  
  332.  
  333.     protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)  
  334.     {
  335.         g.setColor(trackColor);
  336.         g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);    
  337.  
  338.     if(trackHighlight == DECREASE_HIGHLIGHT)    {
  339.         paintDecreaseHighlight(g);
  340.     } 
  341.     else if(trackHighlight == INCREASE_HIGHLIGHT)        {
  342.         paintIncreaseHighlight(g);
  343.     }
  344.     }
  345.  
  346.     
  347.     protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)  
  348.     {
  349.     if(thumbBounds.isEmpty() || !scrollbar.isEnabled())    {
  350.         return;
  351.     }
  352.  
  353.         int w = thumbBounds.width;
  354.         int h = thumbBounds.height;        
  355.  
  356.     g.translate(thumbBounds.x, thumbBounds.y);
  357.  
  358.     g.setColor(thumbDarkShadowColor);
  359.     g.drawRect(0, 0, w-1, h-1);    
  360.     g.setColor(thumbColor);
  361.     g.fillRect(0, 0, w-1, h-1);
  362.         
  363.         g.setColor(thumbHighlightColor);
  364.         g.drawLine(1, 1, 1, h-2);
  365.         g.drawLine(2, 1, w-3, 1);
  366.         
  367.         g.setColor(thumbLightShadowColor);
  368.         g.drawLine(2, h-2, w-2, h-2);
  369.         g.drawLine(w-2, 1, w-2, h-3);
  370.  
  371.     g.translate(-thumbBounds.x, -thumbBounds.y);
  372.     }
  373.  
  374.  
  375.     /** 
  376.      * Return the smallest acceptable size for the thumb.  If the scrollbar
  377.      * becomes so small that this size isn't available, the thumb will be
  378.      * hidden.  
  379.      * <p>
  380.      * <b>Warning </b>: the value returned by this method should not be
  381.      * be modified, it's a shared static constant.
  382.      *
  383.      * @return The smallest acceptable size for the thumb.
  384.      * @see #getMaximumThumbSize
  385.      */
  386.     protected Dimension getMinimumThumbSize() { 
  387.     return minimumThumbSize;
  388.     }
  389.  
  390.     /** 
  391.      * Return the smallest acceptable size for the thumb.  If the scrollbar
  392.      * becomes so small that this size isn't available, the thumb will be
  393.      * hidden.  To create a fixed size thumb one make this
  394.      * method and <code>getMinimumThumbSize</code> return the same value.
  395.      * <p>
  396.      * <b>Warning </b>: the value returned by this method should not be
  397.      * be modified, it's a shared static constant.
  398.      *
  399.      * @return The smallest acceptable size for the thumb.
  400.      * @see #getMinimumThumbSize
  401.      */
  402.     protected Dimension getMaximumThumbSize()    { 
  403.     return maximumThumbSize;
  404.     }
  405.  
  406.  
  407.     /*
  408.      * LayoutManager Implementation
  409.      */
  410.  
  411.     public void addLayoutComponent(String name, Component child) {}
  412.     public void removeLayoutComponent(Component child) {}
  413.     
  414.     public Dimension preferredLayoutSize(Container scrollbarContainer)  {
  415.         return getPreferredSize((JComponent)scrollbarContainer);
  416.     }
  417.     
  418.     public Dimension minimumLayoutSize(Container scrollbarContainer) {
  419.         return getMinimumSize((JComponent)scrollbarContainer);
  420.     }
  421.     
  422.  
  423.     protected void layoutVScrollbar(JScrollBar sb)  
  424.     {
  425.         Dimension sbSize = sb.getSize();
  426.         Insets sbInsets = sb.getInsets();
  427.  
  428.     /*
  429.      * Width and left edge of the buttons and thumb.
  430.      */
  431.     int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
  432.     int itemX = sbInsets.left;
  433.         
  434.         /* Nominal locations of the buttons, assuming their preferred
  435.      * size will fit.
  436.      */
  437.         int decrButtonH = decrButton.getPreferredSize().height;
  438.         int decrButtonY = sbInsets.top;
  439.         
  440.         int incrButtonH = incrButton.getPreferredSize().height;
  441.         int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
  442.         
  443.         /* The thumb must fit within the height left over after we
  444.      * subtract the preferredSize of the buttons and the insets.
  445.      */
  446.         int sbInsetsH = sbInsets.top + sbInsets.bottom;
  447.         int sbButtonsH = decrButtonH + incrButtonH;
  448.         float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
  449.         
  450.         /* Compute the height and origin of the thumb.   The case
  451.      * where the thumb is at the bottom edge is handled specially 
  452.      * to avoid numerical problems in computing thumbY.  Enforce
  453.      * the thumbs min/max dimensions.  If the thumb doesn't
  454.      * fit in the track (trackH) we'll hide it later.
  455.      */
  456.     float min = sb.getMinimum();
  457.     float extent = sb.getVisibleAmount();
  458.     float range = sb.getMaximum() - min;
  459.     float value = sb.getValue();
  460.  
  461.         int thumbH = (range <= 0) 
  462.         ? getMaximumThumbSize().height : (int)(trackH * (extent / range));
  463.     thumbH = Math.max(thumbH, getMinimumThumbSize().height);
  464.     thumbH = Math.min(thumbH, getMaximumThumbSize().height);
  465.         
  466.     int thumbY = incrButtonY - thumbH;  
  467.     if (sb.getValue() < (sb.getMaximum() - sb.getVisibleAmount())) {
  468.         float thumbRange = trackH - thumbH;
  469.         thumbY = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
  470.         thumbY +=  decrButtonY + decrButtonH;
  471.     }
  472.  
  473.         /* If the buttons don't fit, allocate half of the available 
  474.      * space to each and move the lower one (incrButton) down.
  475.      */
  476.         int sbAvailButtonH = (sbSize.height - sbInsetsH);
  477.         if (sbAvailButtonH < sbButtonsH) {
  478.             incrButtonH = decrButtonH = sbAvailButtonH / 2;
  479.             incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
  480.         }
  481.         decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
  482.         incrButton.setBounds(itemX, incrButtonY, itemW, incrButtonH);
  483.  
  484.     /* Update the trackRect field.
  485.      */    
  486.     int itrackY = decrButtonY + decrButtonH;
  487.     int itrackH = incrButtonY - itrackY;
  488.     trackRect.setBounds(itemX, itrackY, itemW, itrackH);
  489.     
  490.     /* If the thumb isn't going to fit, zero it's bounds.  Otherwise
  491.      * make sure it fits between the buttons.  Note that setting the
  492.      * thumbs bounds will cause a repaint.
  493.      */
  494.     if(thumbH >= (int)trackH)    {
  495.         setThumbBounds(0, 0, 0, 0);
  496.     }
  497.     else {
  498.         if ((thumbY + thumbH) > incrButtonY) {
  499.         thumbY = incrButtonY - thumbH;
  500.         }
  501.         if (thumbY  < (decrButtonY + decrButtonH)) {
  502.         thumbY = decrButtonY + decrButtonH + 1;
  503.         }
  504.         setThumbBounds(itemX, thumbY, itemW, thumbH);
  505.     }
  506.     }
  507.     
  508.  
  509.     protected void layoutHScrollbar(JScrollBar sb)  
  510.     {
  511.         Dimension sbSize = sb.getSize();
  512.         Insets sbInsets = sb.getInsets();
  513.         
  514.     /* Height and top edge of the buttons and thumb.
  515.      */
  516.     int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
  517.     int itemY = sbInsets.top;
  518.  
  519.         /* Nominal locations of the buttons, assuming their preferred
  520.      * size will fit.
  521.      */
  522.         int decrButtonW = decrButton.getPreferredSize().width;
  523.         int decrButtonX = sbInsets.left;
  524.         
  525.         int incrButtonW = incrButton.getPreferredSize().width;
  526.         int incrButtonX = sbSize.width - (sbInsets.right + incrButtonW);
  527.         
  528.         /* The thumb must fit within the width left over after we
  529.      * subtract the preferredSize of the buttons and the insets.
  530.      */
  531.         int sbInsetsW = sbInsets.left + sbInsets.right;
  532.         int sbButtonsW = decrButtonW + incrButtonW;
  533.         float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
  534.         
  535.         /* Compute the width and origin of the thumb.  Enforce
  536.      * the thumbs min/max dimensions.  The case where the thumb 
  537.      * is at the right edge is handled specially to avoid numerical 
  538.      * problems in computing thumbX.  If the thumb doesn't
  539.      * fit in the track (trackH) we'll hide it later.
  540.      */
  541.         float min = sb.getMinimum();
  542.         float extent = sb.getVisibleAmount();
  543.         float range = sb.getMaximum() - min;
  544.         float value = sb.getValue();
  545.  
  546.         int thumbW = (range <= 0) 
  547.         ? getMaximumThumbSize().width : (int)(trackW * (extent / range));
  548.         thumbW = Math.max(thumbW, getMinimumThumbSize().width);
  549.         thumbW = Math.min(thumbW, getMaximumThumbSize().width);
  550.         
  551.     int thumbX = incrButtonX - thumbW;
  552.     if (sb.getValue() < (sb.getMaximum() - sb.getVisibleAmount())) {
  553.         float thumbRange = trackW - thumbW;
  554.         thumbX = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
  555.         thumbX +=  decrButtonX + decrButtonW;
  556.     }
  557.  
  558.         /* If the buttons don't fit, allocate half of the available 
  559.          * space to each and move the right one (incrButton) over.
  560.          */
  561.         int sbAvailButtonW = (sbSize.width - sbInsetsW);
  562.         if (sbAvailButtonW < sbButtonsW) {
  563.             incrButtonW = decrButtonW = sbAvailButtonW / 2;
  564.             incrButtonX = sbSize.width - (sbInsets.right + incrButtonW);
  565.         }
  566.         
  567.         decrButton.setBounds(decrButtonX, itemY, decrButtonW, itemH);
  568.         incrButton.setBounds(incrButtonX, itemY, incrButtonW, itemH);
  569.  
  570.     /* Update the trackRect field.
  571.      */    
  572.     int itrackX = decrButtonX + decrButtonW;
  573.     int itrackW = incrButtonX - itrackX;
  574.     trackRect.setBounds(itrackX, itemY, itrackW, itemH);
  575.  
  576.     /* Make sure the thumb fits between the buttons.  Note 
  577.      * that setting the thumbs bounds causes a repaint.
  578.      */
  579.     if (thumbW >= (int)trackW) {
  580.         setThumbBounds(0, 0, 0, 0);
  581.     }
  582.     else {
  583.         if (thumbX + thumbW > incrButtonX) {
  584.         thumbX = incrButtonX - thumbW;
  585.         }
  586.         if (thumbX  < decrButtonX + decrButtonW) {
  587.         thumbX = decrButtonX + decrButtonW + 1;
  588.         }
  589.         setThumbBounds(thumbX, itemY, thumbW, itemH);
  590.     }
  591.     }
  592.     
  593.  
  594.     public void layoutContainer(Container scrollbarContainer) 
  595.     {
  596.     /* If the user is dragging the value, we'll assume that the 
  597.      * scrollbars layout is OK modulo the thumb which is being 
  598.      * handled by the dragging code.
  599.      */
  600.     if (isDragging) {
  601.         return;
  602.     }
  603.  
  604.         JScrollBar scrollbar = (JScrollBar)scrollbarContainer;
  605.         switch (scrollbar.getOrientation()) {
  606.         case JScrollBar.VERTICAL:
  607.             layoutVScrollbar(scrollbar);
  608.             break;
  609.             
  610.         case JScrollBar.HORIZONTAL:
  611.             layoutHScrollbar(scrollbar);
  612.             break;
  613.         }
  614.     }
  615.  
  616.  
  617.     /**
  618.      * Set the bounds of the thumb and force a repaint that includes
  619.      * the old thumbBounds and the new one.
  620.      *
  621.      * @see #getThumbBounds
  622.      */
  623.     protected void setThumbBounds(int x, int y, int width, int height)
  624.     {
  625.     /* If the thumbs bounds haven't changed, we're done.
  626.      */
  627.     if ((thumbRect.x == x) && 
  628.         (thumbRect.y == y) && 
  629.         (thumbRect.width == width) && 
  630.         (thumbRect.height == height)) {
  631.         return;
  632.     }
  633.  
  634.     /* Update thumbRect, and repaint the union of x,y,w,h and 
  635.      * the old thumbRect.
  636.      */
  637.     int minX = Math.min(x, thumbRect.x);
  638.     int minY = Math.min(y, thumbRect.y);
  639.     int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
  640.     int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
  641.  
  642.     thumbRect.setBounds(x, y, width, height);
  643.     scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
  644.     }
  645.  
  646.  
  647.     /**
  648.      * Return the current size/location of the thumb.
  649.      * <p>
  650.      * <b>Warning </b>: the value returned by this method should not be
  651.      * be modified, it's a reference to the actual rectangle, not a copy.
  652.      *
  653.      * @return The current size/location of the thumb.
  654.      * @see #setThumbBounds
  655.      */
  656.     protected Rectangle getThumbBounds() {
  657.     return thumbRect;
  658.     }
  659.  
  660.  
  661.     /**
  662.      * Return the current bounds of the track, i.e. the space in between
  663.      * the increment and decrement buttons, less the insets.  The value
  664.      * returned by this method is updated each time the scrollbar is
  665.      * layed out (validated).
  666.      * <p>
  667.      * <b>Warning </b>: the value returned by this method should not be
  668.      * be modified, it's a reference to the actual rectangle, not a copy.
  669.      *
  670.      * @return The current bounds of the scrollbar track.
  671.      * @see #layoutContainer
  672.      */
  673.     protected Rectangle getTrackBounds() {
  674.     return trackRect;
  675.     }
  676.  
  677.  
  678.  
  679.     protected void scrollByBlock(int direction)
  680.     {
  681.     synchronized(scrollbar)    {
  682.         int oldValue = scrollbar.getValue();
  683.         int blockIncrement = scrollbar.getBlockIncrement(direction);
  684.         int delta = blockIncrement * ((direction > 0) ? +1 : -1);
  685.  
  686.         scrollbar.setValue(oldValue + delta);            
  687.         trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
  688.         Rectangle dirtyRect = getTrackBounds();
  689.         scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
  690.     }
  691.     }
  692.     
  693.  
  694.     protected void scrollByUnit(int direction)    {
  695.     synchronized(scrollbar)    {
  696.  
  697.     int delta;
  698.     if(direction > 0)
  699.             delta = scrollbar.getUnitIncrement(direction);
  700.     else
  701.             delta = -scrollbar.getUnitIncrement(direction);
  702.         scrollbar.setValue(delta + scrollbar.getValue()); 
  703.     }
  704.     }
  705.           
  706.     /**
  707.      * A listener to listen for model changes.
  708.      * <p>
  709.      * Warning: serialized objects of this class will not be compatible with
  710.      * future swing releases.  The current serialization support is appropriate
  711.      * for short term storage or RMI between Swing1.0 applications.  It will
  712.      * not be possible to load serialized Swing1.0 objects with future releases
  713.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  714.      * baseline for the serialized form of Swing objects.
  715.      *
  716.      */
  717.     protected class ModelListener implements ChangeListener, Serializable {
  718.         public void stateChanged(ChangeEvent e) {
  719.         layoutContainer(scrollbar);
  720.         }
  721.     }
  722.  
  723.  
  724.     /**
  725.      * Track mouse drags.
  726.      * <p>
  727.      * Warning: serialized objects of this class will not be compatible with
  728.      * future swing releases.  The current serialization support is appropriate
  729.      * for short term storage or RMI between Swing1.0 applications.  It will
  730.      * not be possible to load serialized Swing1.0 objects with future releases
  731.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  732.      * baseline for the serialized form of Swing objects.
  733.      */
  734.     protected class TrackListener
  735.         extends MouseAdapter implements MouseMotionListener, Serializable
  736.     {
  737.     protected transient int offset;
  738.     protected transient int currentMouseX, currentMouseY;
  739.         
  740.         public void mouseReleased(MouseEvent e)
  741.         {
  742.         if(!scrollbar.isEnabled())
  743.         return;
  744.  
  745.         if(trackHighlight != NO_HIGHLIGHT) {
  746.         Rectangle r = getTrackBounds();
  747.         scrollbar.repaint(r.x, r.y, r.width, r.height);
  748.         }
  749.  
  750.         trackHighlight = NO_HIGHLIGHT;
  751.         isDragging = false;
  752.         offset = 0;
  753.         scrollTimer.stop();
  754.         scrollbar.setValueIsAdjusting(false);
  755.     }
  756.         
  757.  
  758.         /**
  759.      * If the mouse is pressed above the "thumb" component
  760.      * then reduce the scrollbars value by one page ("page up"), 
  761.      * otherwise increase it by one page.  If there is no 
  762.      * thumb then page up if the mouse is in the upper half
  763.      * of the track.
  764.      */
  765.         public void mousePressed(MouseEvent e) 
  766.     {
  767.         if(!scrollbar.isEnabled())
  768.         return;
  769.  
  770.         scrollbar.setValueIsAdjusting(true);
  771.  
  772.             currentMouseX = e.getX();
  773.             currentMouseY = e.getY();
  774.     
  775.         // Clicked in the Thumb area?
  776.         if(getThumbBounds().contains(currentMouseX, currentMouseY))    {
  777.                 switch (scrollbar.getOrientation()) {
  778.                 case JScrollBar.VERTICAL:
  779.             offset = currentMouseY - getThumbBounds().y;
  780.                     break;
  781.                 case JScrollBar.HORIZONTAL:
  782.             offset = currentMouseX - getThumbBounds().x;
  783.                     break;
  784.                 }
  785.         isDragging = true;
  786.         return;
  787.         }            
  788.         isDragging = false;
  789.                             
  790.             Dimension sbSize = scrollbar.getSize();
  791.             int direction = +1;
  792.  
  793.             switch (scrollbar.getOrientation()) {
  794.             case JScrollBar.VERTICAL:
  795.                 if (getThumbBounds().isEmpty()) {
  796.                     int scrollbarCenter = sbSize.height / 2;
  797.             direction = (currentMouseY < scrollbarCenter) ? -1 : +1;
  798.                 } else {
  799.                     int thumbY = getThumbBounds().y;
  800.             direction = (currentMouseY < thumbY) ? -1 : +1;
  801.                 }
  802.                 break;                    
  803.             case JScrollBar.HORIZONTAL:
  804.                 if (getThumbBounds().isEmpty()) {
  805.                     int scrollbarCenter = sbSize.width / 2;
  806.                     direction = (currentMouseX < scrollbarCenter) ? -1 : +1;
  807.                 } else {
  808.                     int thumbX = getThumbBounds().x;
  809.                     direction = (currentMouseX < thumbX) ? -1 : +1;
  810.                 }
  811.                 break;
  812.             }
  813.         scrollByBlock(direction);
  814.         if(!getThumbBounds().contains(currentMouseX, currentMouseY))    {
  815.         scrollTimer.stop();
  816.         scrollListener.setDirection(direction);
  817.         scrollListener.setScrollByBlock(true);
  818.         scrollTimer.start();
  819.         }
  820.         }
  821.         
  822.  
  823.         /** 
  824.      * Set the models value to the position of the top/left
  825.      * of the thumb relative to the origin of the track.
  826.      */
  827.     public void mouseDragged(MouseEvent e)
  828.     {
  829.         if(!scrollbar.isEnabled() || !isDragging) {
  830.         return;
  831.         }
  832.  
  833.         BoundedRangeModel model = scrollbar.getModel();
  834.         Rectangle thumbR = getThumbBounds();
  835.         float trackLength;
  836.         int thumbMin, thumbMax, thumbPos;
  837.  
  838.             if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  839.         thumbMin = decrButton.getY() + decrButton.getHeight();
  840.         thumbMax = incrButton.getY() - getThumbBounds().height;
  841.         thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getY() - offset)));
  842.         setThumbBounds(thumbR.x, thumbPos, thumbR.width, thumbR.height);
  843.         trackLength = getTrackBounds().height;
  844.         }
  845.         else {
  846.         thumbMin = decrButton.getX() + decrButton.getWidth();
  847.         thumbMax = incrButton.getX() - getThumbBounds().width;
  848.         thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getX() - offset)));
  849.         setThumbBounds(thumbPos, thumbR.y, thumbR.width, thumbR.height);
  850.         trackLength = getTrackBounds().width;
  851.         }
  852.  
  853.         /* Set the scrollbars value.  If the thumb has reached the end of
  854.          * the scrollbar, then just set the value to its maximum.  Otherwise
  855.          * compute the value as accurately as possible.
  856.          */
  857.         if (thumbPos == thumbMax) {
  858.         scrollbar.setValue(model.getMaximum() - model.getExtent());
  859.         }
  860.         else {
  861.         float valueMax = model.getMaximum() - model.getExtent();
  862.         float valueRange = valueMax - model.getMinimum();
  863.         float thumbValue = thumbPos - thumbMin;
  864.         float thumbRange = thumbMax - thumbMin;
  865.         int value = (int)(0.5 + ((thumbValue / thumbRange) * valueRange));
  866.         scrollbar.setValue(value + model.getMinimum());
  867.         }
  868.         }
  869.         
  870.     public void mouseMoved(MouseEvent e) {
  871.     }
  872.     }
  873.         
  874.  
  875.     /**
  876.      * Listener for cursor keys.
  877.      * <p>
  878.      * Warning: serialized objects of this class will not be compatible with
  879.      * future swing releases.  The current serialization support is appropriate
  880.      * for short term storage or RMI between Swing1.0 applications.  It will
  881.      * not be possible to load serialized Swing1.0 objects with future releases
  882.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  883.      * baseline for the serialized form of Swing objects.
  884.      */
  885.     protected class ArrowButtonListener extends MouseAdapter implements Serializable
  886.     {        
  887.     // Because we are handling both mousePressed and Actions
  888.     // we need to make sure we don't fire under both conditions.
  889.     // (keyfocus on scrollbars causes action without mousePress
  890.     boolean handledEvent;
  891.     
  892.     public void mousePressed(MouseEvent e)        {
  893.         if(!scrollbar.isEnabled())
  894.         return;
  895.  
  896.         int direction = (e.getSource() == incrButton) ? 1 : -1;
  897.         
  898.         scrollByUnit(direction);
  899.         scrollTimer.stop();
  900.         scrollListener.setDirection(direction);
  901.         scrollListener.setScrollByBlock(false);
  902.         scrollTimer.start();
  903.  
  904.         handledEvent = true;    
  905.     }
  906.  
  907.     public void mouseReleased(MouseEvent e)        {
  908.         scrollTimer.stop();
  909.         handledEvent = false;
  910.         scrollbar.setValueIsAdjusting(false);
  911.     }
  912.     }
  913.  
  914.  
  915.     /**
  916.      * Listener for scrolling events intiated in the
  917.      * ScrollPane.
  918.      * <p>
  919.      * Warning: serialized objects of this class will not be compatible with
  920.      * future swing releases.  The current serialization support is appropriate
  921.      * for short term storage or RMI between Swing1.0 applications.  It will
  922.      * not be possible to load serialized Swing1.0 objects with future releases
  923.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  924.      * baseline for the serialized form of Swing objects.
  925.      */
  926.     protected class ScrollListener implements ActionListener, Serializable
  927.     {
  928.     int direction = +1;
  929.     boolean useBlockIncrement;
  930.  
  931.     public ScrollListener()    {
  932.         direction = +1;
  933.         useBlockIncrement = false;
  934.     }
  935.  
  936.         public ScrollListener(int dir, boolean block)    {
  937.         direction = dir;
  938.         useBlockIncrement = block;
  939.     }
  940.     
  941.     public void setDirection(int direction) { this.direction = direction; }
  942.     public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
  943.                     
  944.     public void actionPerformed(ActionEvent e) {
  945.         if(useBlockIncrement)    {
  946.         scrollByBlock(direction);        
  947.         // Stop scrolling if the thumb catches up with the mouse
  948.         if(scrollbar.getOrientation() == JScrollBar.VERTICAL)    {
  949.             if(direction > 0)    {
  950.             if(getThumbBounds().y + getThumbBounds().height 
  951.                 >= trackListener.currentMouseY)
  952.                     ((Timer)e.getSource()).stop();
  953.             } else if(getThumbBounds().y <= trackListener.currentMouseY)    {
  954.             ((Timer)e.getSource()).stop();
  955.             }
  956.         } else {
  957.             if(direction > 0)    {
  958.             if(getThumbBounds().x + getThumbBounds().width 
  959.                 >= trackListener.currentMouseX)
  960.                     ((Timer)e.getSource()).stop();
  961.             } else if(getThumbBounds().x <= trackListener.currentMouseX)    {
  962.                 ((Timer)e.getSource()).stop();
  963.             }
  964.             }
  965.         } else {
  966.         scrollByUnit(direction);
  967.         }
  968.  
  969.         if(direction > 0 
  970.         && scrollbar.getValue()+scrollbar.getVisibleAmount() 
  971.             >= scrollbar.getMaximum())
  972.         ((Timer)e.getSource()).stop();
  973.         else if(direction < 0 
  974.         && scrollbar.getValue() <= scrollbar.getMinimum())
  975.             ((Timer)e.getSource()).stop();
  976.     }
  977.     }
  978. }
  979.             
  980.             
  981.