private void moveWithinGridRange(JGrid grid, int dx, int dy, boolean changeLead) {
   if (changeLead) {
     leadRow = clipToRange(leadRow + dy, 0, grid.getRowCount());
     leadColumn = clipToRange(leadColumn + dx, 0, grid.getColumnCount());
   } else {
     anchorRow = clipToRange(anchorRow + dy, 0, grid.getRowCount());
     anchorColumn = clipToRange(anchorColumn + dx, 0, grid.getColumnCount());
   }
 }
    public void actionPerformed(ActionEvent e) {
      JGrid grid = (JGrid) e.getSource();
      if (toLimit) {
        if (vertically) {
          int rowCount = grid.getRowCount();
          this.dx = 0;
          this.dy = forwards ? rowCount : -rowCount;
        } else {
          int colCount = grid.getColumnCount();
          this.dx = forwards ? colCount : -colCount;
          this.dy = 0;
        }
      } else {
        if (!(grid.getParent().getParent() instanceof JScrollPane)) {
          return;
        }

        Dimension delta = grid.getParent().getSize();
        SelectionModel sm = grid.getSelectionModel();

        int start = 0;
        if (vertically) {
          start = (extend) ? sm.getLeadRow() : sm.getAnchorRow();
        } else {
          start = (extend) ? sm.getLeadColumn() : sm.getAnchorColumn();
        }

        if (vertically) {
          Rectangle r = grid.getCellBounds(start, 0);
          r.y += forwards ? delta.height : -delta.height;
          this.dx = 0;
          int newRow = grid.rowAtPoint(r.getLocation());
          if (newRow == -1 && forwards) {
            newRow = grid.getRowCount();
          }
          this.dy = newRow - start;
        } else {
          Rectangle r = grid.getCellBounds(0, start);
          r.x += forwards ? delta.width : -delta.width;
          int newColumn = grid.columnAtPoint(r.getLocation());
          if (newColumn == -1 && forwards) {
            newColumn = grid.getColumnCount();
          }
          this.dx = newColumn - start;
          this.dy = 0;
        }
      }
      super.actionPerformed(e);
    }
    private boolean moveWithinSelectedRange(JGrid grid, int dx, int dy, boolean ignoreCarry) {
      SelectionModel sm = grid.getSelectionModel();

      int newAnchorRow = anchorRow + dy;
      int newAnchorColumn = anchorColumn + dx;

      int rowSgn;
      int colSgn;
      int rowCount = selectionSpan(sm, SwingConstants.VERTICAL);
      int columnCount = selectionSpan(sm, SwingConstants.HORIZONTAL);

      boolean canStayInSelection = (rowCount * columnCount > 1);
      if (canStayInSelection) {
        rowSgn = compare(newAnchorRow, sm, SwingConstants.VERTICAL);
        colSgn = compare(newAnchorColumn, sm, SwingConstants.HORIZONTAL);
      } else {
        // If there is only one selected cell, there is no point
        // in trying to stay within the selected area. Move outside
        // the selection, wrapping at the table boundaries.
        rowCount = grid.getRowCount();
        columnCount = grid.getColumnCount();
        rowSgn = compare(newAnchorRow, 0, rowCount);
        colSgn = compare(newAnchorColumn, 0, columnCount);
      }

      anchorRow = newAnchorRow - rowCount * rowSgn;
      anchorColumn = newAnchorColumn - columnCount * colSgn;

      if (!ignoreCarry) {
        return moveWithinSelectedRange(grid, rowSgn, colSgn, true);
      }
      return canStayInSelection;
    }
  public void paint(Graphics g, JComponent c) {
    if (grid.getRowCount() <= 0 || grid.getColumnCount() <= 0) {
      return; // nothing to paint
    }

    Rectangle clip = g.getClipBounds();
    Point minLocation = clip.getLocation();
    Point maxLocation = new Point(clip.x + clip.width - 1, clip.y + clip.height - 1);
    int rowMin = grid.rowAtPoint(minLocation);
    int rowMax = grid.rowAtPoint(maxLocation);
    // This should never happen.
    if (rowMin == -1) {
      rowMin = 0;
    }
    // If the spread does not have enough rows to fill the view we'll get -1.
    // Replace this with the index of the last row.
    if (rowMax == -1) {
      rowMax = grid.getRowCount() - 1;
    }
    int colMin = grid.columnAtPoint(minLocation);
    int colMax = grid.columnAtPoint(maxLocation);
    // This should never happen.
    if (colMin == -1) {
      colMin = 0;
    }
    // If the spread does not have enough columns to fill the view we'll get -1.
    // Replace this with the index of the last column.
    if (colMax == -1) {
      colMax = grid.getColumnCount() - 1;
    }

    // Paint cells
    paintCells(g, rowMin, rowMax, colMin, colMax);

    // Paint grid
    paintGrid(g, rowMin, rowMax, colMin, colMax);

    // Paint spans
    paintSpans(g, rowMin, rowMax, colMin, colMax);

    // Paint borders
    paintBorders(g, rowMin, rowMax, colMin, colMax);

    // Paint editor
    paintEditor(g);
  }
  public Dimension getMaximumSize(JComponent c) {
    int lastRow = grid.getRowCount() - 1;
    int lastCol = grid.getColumnCount() - 1;

    Rectangle cellBounds = grid.getCellBounds(lastRow, lastCol);
    int width = cellBounds.x + cellBounds.width;
    int height = cellBounds.y + cellBounds.height;

    return new Dimension(width, height);
  }
 /*
  * Borders are handled by the UI since they are shared between cells
  * and therefore must overlay after the painting of the cells
  */
 private void paintBorders(Graphics g, int rowMin, int rowMax, int colMin, int colMax) {
   // Include borders from adjacent rows/columns of the clip region
   rowMin = Math.max(0, rowMin - 1);
   rowMax = Math.min(grid.getRowCount() - 1, rowMax + 1);
   colMin = Math.max(0, colMin - 1);
   colMax = Math.min(grid.getColumnCount() - 1, colMax + 1);
   for (int row = rowMin; row <= rowMax; row++) {
     for (int column = colMin; column <= colMax; column++) {
       Rectangle cellRect = grid.getCellBounds(row, column);
       paintBorder(g, cellRect, row, column);
     }
   }
 }
  public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
    // Check there is something to print
    if (pageIndex >= maxNumPage || grid.getRowCount() == 0 || grid.getColumnCount() == 0) {
      return Printable.NO_SUCH_PAGE;
    }

    Graphics2D g2d = (Graphics2D) g;
    g2d.translate((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY());

    int pageWidth = (int) pageFormat.getImageableWidth();
    int pageHeight = (int) pageFormat.getImageableHeight();
    int gridWidth = grid.getColumnModel().getTotalSize();
    int gridHeight = grid.getRowModel().getTotalSize();

    // Check there is something visible to print
    if (gridWidth == 0 || gridHeight == 0) {
      return Printable.NO_SUCH_PAGE;
    }

    // Calculate page to print
    int pageColumn = pageIndex % pageCols;
    int pageRow = pageIndex / pageCols;

    // Calculate scale to make the grid fit in maxNumPage
    float xFitPageScale = (pageWidth * pageCols) / (float) gridWidth;
    float yFitPageScale = (pageHeight * pageRows) / (float) gridHeight;
    float minScale = Math.min(xFitPageScale, yFitPageScale);

    // Scale the print area
    int pageX = (int) ((pageColumn * pageWidth) / minScale);
    int pageY = (int) ((pageRow * pageHeight) / minScale);
    pageWidth = (int) (pageWidth / minScale);
    pageHeight = (int) (pageHeight / minScale);

    // Get clip region for page
    Point p = new Point(pageX, pageY);
    int firstRow = grid.rowAtPoint(p);
    int firstCol = grid.columnAtPoint(p);
    int x1 = grid.getColumnPosition(firstCol);
    int y1 = grid.getRowPosition(firstRow);
    int x2 = x1 + pageWidth;
    int y2 = y1 + pageHeight;
    p = new Point(x2, y2);
    int lastRow = grid.rowAtPoint(p);
    int lastCol = grid.columnAtPoint(p);
    if (grid.getRowPosition(lastRow) + grid.getRowHeight(lastRow) > y2) {
      y2 = grid.getRowPosition(lastRow);
    } else {
      y2 = grid.getRowPosition(lastRow) + grid.getRowHeight(lastRow);
    }
    if (grid.getColumnPosition(lastCol) + grid.getColumnWidth(lastCol) > x2) {
      x2 = grid.getColumnPosition(lastCol);
    } else {
      x2 = grid.getColumnPosition(lastCol) + grid.getColumnWidth(lastCol);
    }
    int x = x1;
    int y = y1;
    int width = x2 - x1;
    int height = y2 - y1;

    SelectionSettings selectionSettings = new SelectionSettings(grid.getSelectionModel());
    selectionSettings.clear();

    // Print the page
    g2d.scale(minScale, minScale);
    g2d.translate(-x, -y);
    g2d.setClip(x, y, width, height);
    grid.print(g2d);

    selectionSettings.restore();

    return Printable.PAGE_EXISTS;
  }