@Override
  public void draw(
      Graphics2D g2,
      XYPlot plot,
      Rectangle2D dataArea,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      int rendererIndex,
      PlotRenderingInfo info) {

    Rectangle2D box = chartPanel.getScreenDataArea();
    float sx = (float) plot.getDomainAxis().valueToJava2D(x, box, plot.getDomainAxisEdge());
    float maxXLim = (float) box.getWidth() - g2.getFontMetrics().stringWidth(text);
    if (sx > maxXLim) {
      sx = maxXLim;
    }
    if (sx < box.getMinX()) {
      sx = (float) box.getMinX();
    }

    float sy =
        (float)
            plot.getRangeAxis()
                .valueToJava2D(y, chartPanel.getScreenDataArea(), plot.getRangeAxisEdge());
    g2.setTransform(new AffineTransform());
    g2.setColor(color);
    g2.setFont(font);
    g2.drawString(text, sx, sy);
  }
  @Override
  public void mouseDragged(MouseEvent mouseEvent) {
    if (!mouseEvent.isControlDown()) {
      return;
    }

    if (oldx > -1 && oldy > -1) {
      int xdif = mouseEvent.getX() - oldx;
      int ydif = mouseEvent.getY() - oldy;

      final Rectangle2D scaledDataArea = chartPanel.getScreenDataArea();

      ValueAxis[] domAxes = getPlotAxis(chartPanel.getChart(), !axesSwaped);
      if (domAxes != null) {
        double[] xDelta = new double[domAxes.length];
        for (int i = 0; i < domAxes.length; i++) {
          xDelta[i] = xdif * domAxes[i].getRange().getLength() / (scaledDataArea.getWidth());
        }
        for (int i = 0; i < domAxes.length; i++) {
          domAxes[i].setRange(
              domAxes[i].getLowerBound() - xDelta[i], domAxes[i].getUpperBound() - xDelta[i]);
        }
      }

      ValueAxis[] rngAxes = getPlotAxis(chartPanel.getChart(), axesSwaped);
      if (rngAxes != null) {
        double[] yDelta = new double[rngAxes.length];
        for (int i = 0; i < rngAxes.length; i++) {
          yDelta[i] = ydif * rngAxes[i].getRange().getLength() / (scaledDataArea.getHeight());
        }
        if (!onlyXShift) {
          for (int i = 0; i < rngAxes.length; i++) {
            rngAxes[i].setRange(
                rngAxes[i].getLowerBound() + yDelta[i], rngAxes[i].getUpperBound() + yDelta[i]);
          }
        }
      }
    }

    oldx = mouseEvent.getX();
    oldy = mouseEvent.getY();
  }
  /** Pan / Shifts a plot if the arrow keys are pressed. */
  public void keyPressed(KeyEvent e) {
    if (!plotSupported) {
      return;
    }

    int keyCode = e.getKeyCode();

    // we're only interested in arrows (code 37,38,39,40)
    if ((keyCode < 37) || (keyCode > 40)) {
      return;
    }

    // The axes we're gonna shift
    ValueAxis[] axes = null;

    boolean domainShift = false; // used for PAN_FIXED
    // Calculations for the domain axis
    if ((keyCode == KeyEvent.VK_LEFT) || (keyCode == KeyEvent.VK_RIGHT)) {
      axes = getPlotAxis(chartPanel.getChart(), !axesSwaped);
      domainShift = true;
    }
    // Calculations for the range axis
    else {
      axes = getPlotAxis(chartPanel.getChart(), axesSwaped);
    }

    // Delta is the amount we'll shift in axes units.
    double[] delta = new double[axes.length];

    // Let's calculate 'delta', the amount by which we'll shift the plot
    for (int i = 0; i < axes.length; i++) {
      switch (shiftType) {
        case SHIFT_PERCENTUAL:
          delta[i] = (axes[i].getUpperBound() - axes[i].getLowerBound()) / 100.0;
          break;
        case SHIFT_FIXED:
          delta[i] = (domainShift ? fixedDomainShiftUnits : fixedRangeShiftUnits);
          break;
        case SHIFT_PIXEL: // also the default
        default:
          // Let's find out what's the range for 1 pixel.
          final Rectangle2D scaledDataArea = chartPanel.getScreenDataArea();
          delta[i] = axes[i].getRange().getLength() / (scaledDataArea.getWidth());
          break;
      }
    }

    // Shift modifier multiplies delta by 10
    if (e.isShiftDown()) {
      for (int i = 0; i < delta.length; i++) {
        delta[i] *= 10;
      }
    }

    for (int i = 0; i < axes.length; i++) {
      switch (keyCode) {
        case KeyEvent.VK_LEFT:
        case KeyEvent.VK_DOWN:
          axes[i].setRange(axes[i].getLowerBound() - delta[i], axes[i].getUpperBound() - delta[i]);
          break;
        case KeyEvent.VK_UP:
        case KeyEvent.VK_RIGHT:
          axes[i].setRange(axes[i].getLowerBound() + delta[i], axes[i].getUpperBound() + delta[i]);
          break;
      }
    }
  }