/**
   * Gets the scale factor that allows us to translate pixel values to scroll bar values and vice
   * versa. This assumes that the range of pixels spans from the last pixel of
   * <tt>scrollUpButton</tt> to the first pixel of <tt>scrollDownButton</tt> and excludes the pixels
   * taken up by <tt>handle</tt>.
   *
   * <p>To map from scroll bar values (<i>real values</i>) to pixel values, you multiply by the
   * value scale. To map from pixel values back to real values, you divide by the value scale.
   *
   * @return <tt>&lt;number of legal pixel values&gt; / &lt;number of legal real values&gt;</tt>
   */
  private float getValueScale() {
    ScrollBar scrollBar = (ScrollBar) getComponent();

    float valueScale;

    int start = scrollBar.getStart();
    int end = scrollBar.getEnd();
    int extent = scrollBar.getExtent();
    int maxLegalRealValue = end - extent;

    int numLegalRealValues = maxLegalRealValue - start + 1;
    int numLegalPixelValues;

    if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
      int availableWidth = getWidth() - scrollUpButton.getWidth() - scrollDownButton.getWidth() + 2;
      numLegalPixelValues = availableWidth - handle.getWidth() + 1;
    } else {
      int availableHeight =
          getHeight() - scrollUpButton.getHeight() - scrollDownButton.getHeight() + 2;
      numLegalPixelValues = availableHeight - handle.getHeight() + 1;
    }

    // the number of segments is one less than the number of values
    valueScale = numLegalPixelValues / ((float) numLegalRealValues - 1);

    return valueScale;
  }
  /**
   * Initiates automatic block scrolling. This only happens if the handle is visible since whether
   * the user clicked before or after the handle determines the direction of the scrolling.
   *
   * @param component The scroll bar.
   * @param button The button that was pressed.
   * @param x The x-coordinate of the event in the scroll bar's coordinate space.
   * @param y The y-coordinate of the event in the scroll bar's coordinate space.
   */
  @Override
  public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
    boolean consumed = super.mouseDown(component, button, x, y);

    if (button == Mouse.Button.LEFT && handle.isVisible()) {
      ScrollBar scrollBar = (ScrollBar) getComponent();

      // Begin automatic block scrolling. Calculate the direction of
      // the scroll by checking to see if the user pressed the mouse
      // in the area "before" the handle or "after" it.
      int direction;
      int realStopValue;

      if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
        direction = x < handle.getX() ? -1 : 1;

        int pixelStopValue = x - scrollUpButton.getWidth() + 1;

        if (direction == 1) {
          // If we're scrolling down, account for the width of the
          // handle in our pixel stop value so that we stop as soon
          // as the *bottom* of the handle reaches our click point
          pixelStopValue -= handle.getWidth();
        }

        realStopValue = (int) (pixelStopValue / getValueScale());
      } else {
        direction = y < handle.getY() ? -1 : 1;

        int pixelStopValue = y - scrollUpButton.getHeight() + 1;

        if (direction == 1) {
          // If we're scrolling down, account for the height of the
          // handle in our pixel stop value so that we stop as soon
          // as the *bottom* of the handle reaches our click point
          pixelStopValue -= handle.getHeight();
        }

        realStopValue = (int) (pixelStopValue / getValueScale());
      }

      // Start the automatic scroller
      automaticScroller.start(direction, Mouse.ScrollType.BLOCK, realStopValue);
      consumed = true;
    }

    return consumed;
  }