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);
    }
  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 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;
  }