1.  
  2.  
  3. import javax.swing.*;
  4. import javax.swing.border.LineBorder;
  5. import java.awt.*;
  6. import java.awt.event.*;
  7. import java.awt.image.BufferedImage;
  8. import java.awt.image.ImageObserver;
  9.  
  10. public class JScrollNavigator extends JPanel implements ComponentListener, AdjustmentListener {
  11.  
  12.     private JScrollPane jScrollPane;
  13.     private boolean blockRepaint = false;
  14.  
  15.     private NavBox overBox = new NavBox();
  16.  
  17.     boolean isAdjusting = false;
  18.  
  19.     public JScrollNavigator() {
  20.  
  21.         this.setSize(new Dimension(80, 100));
  22.  
  23.         setBackground(Color.BLACK);
  24.  
  25.         setLayout(null);
  26.         add(overBox);
  27.  
  28.         overBox.setOpaque(false);
  29.  
  30.         addComponentListener(new ComponentAdapter() {
  31.  
  32.             public void componentResized(ComponentEvent e) {
  33.                 updateOverBox();
  34.             }
  35.  
  36.         });
  37.  
  38.         overBox.addComponentListener(this);
  39.     }
  40.  
  41.     public void setJScrollPane(JScrollPane jScrollPane) {
  42.         this.jScrollPane = jScrollPane;
  43.  
  44.         Component view = jScrollPane.getViewport().getView();
  45.  
  46.         if(view != null) {
  47.             view.addComponentListener(this);
  48.             jScrollPane.getViewport().addComponentListener(this);
  49.             jScrollPane.getHorizontalScrollBar().addAdjustmentListener(this);
  50.             jScrollPane.getVerticalScrollBar().addAdjustmentListener(this);
  51.             updateOverBox();
  52.         }
  53.  
  54.     }
  55.  
  56.     private void updateOverBox() {
  57.         isAdjusting = true;
  58.  
  59.         JViewport viewport = this.jScrollPane.getViewport();
  60.  
  61.         Dimension d = viewport.getViewSize();
  62.         Rectangle vRect = viewport.getViewRect();
  63.  
  64.         int vWidth = d.width;
  65.         int vHeight = d.height;
  66.  
  67.         int w = getWidth();
  68.         int h = getHeight();
  69.  
  70.         float xMult = (float) w / vWidth;
  71.         float yMult = (float) h / vHeight;
  72.  
  73.         int newX = (int) (vRect.x * xMult);
  74.         int newY = (int) (vRect.y * yMult);
  75.         int newW = (int) (vRect.width * xMult);
  76.         int newH = (int) (vRect.height * yMult);
  77.  
  78.         overBox.setLocation(newX, newY);
  79.         overBox.setSize(newW, newH);
  80.  
  81.         isAdjusting = false;
  82.  
  83.     }
  84.  
  85.     @Override
  86.     protected void paintComponent(final Graphics g) {
  87.         super.paintComponent(g);
  88.         if(!blockRepaint){
  89.             final JComponent view = (JComponent)jScrollPane.getViewport().getView();
  90.             BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
  91.             Graphics2D g2d = img.createGraphics();
  92.             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  93.    
  94.             // Paint JScrollPane view to off-screen image and then scale.
  95.             // It is this action that causes the display corruption!
  96.             view.paint(g2d);
  97.             ImageObserver io = new ImageObserver() {
  98.                
  99.                 @Override
  100.                 public boolean imageUpdate(Image img, int infoflags, int x, int y,int width, int height) {
  101.                     boolean result = true;
  102.                     System.out.println("Hi" + g.drawImage(img, 0, 0, null));
  103.                     if((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS){
  104.                         blockRepaint = false;
  105.                         result = false;
  106.                         System.out.println("BlockRemoved");
  107.                     }if((infoflags & ImageObserver.ABORT) == ImageObserver.ABORT){
  108.                         System.out.println("ABORT");
  109.                     }if((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS){
  110.                         System.out.println("ALLBITS");
  111.                     }if((infoflags & ImageObserver.ERROR) == ImageObserver.ERROR){
  112.                         System.out.println("ERROR");
  113.                     }if((infoflags & ImageObserver.FRAMEBITS) == ImageObserver.FRAMEBITS){
  114.                         System.out.println("FRAMEBITS");
  115.                         blockRepaint = false;
  116.                         result = false;
  117.                     }if((infoflags & ImageObserver.HEIGHT) == ImageObserver.HEIGHT){
  118.                         System.out.println("HEIGHT");
  119.                     }if((infoflags & ImageObserver.PROPERTIES) == ImageObserver.PROPERTIES){
  120.                         System.out.println("PROPERTIES");
  121.                     }if((infoflags & ImageObserver.SOMEBITS) == ImageObserver.SOMEBITS){
  122.                         System.out.println("SOMEBITS");
  123.                     }if((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH){
  124.                         System.out.println("WIDTH");
  125.                     }
  126.                    
  127.                     return result;
  128.                 }
  129.             };
  130.    
  131.             //System.out.println(g2d.drawImage(img, view.getX(), view.getY(), null));
  132.             Image scaled = img.getScaledInstance(getWidth(), getHeight(), java.awt.Image.SCALE_FAST);
  133.             blockRepaint = g.drawImage(scaled, 0, 0, io);
  134.             System.out.println(blockRepaint);
  135.         }
  136.     }
  137.  
  138.     public void componentResized(ComponentEvent e) {
  139.         if(e.getSource() == jScrollPane.getViewport() || e.getSource() == jScrollPane.getViewport().getView()) {
  140.             updateOverBox();
  141.         }
  142.     }
  143.  
  144.     public void componentMoved(ComponentEvent e) {
  145.         if(e.getSource() == overBox) {
  146.             isAdjusting = true;
  147.  
  148.             Rectangle r = overBox.getBounds();
  149.  
  150.             JViewport viewport = this.jScrollPane.getViewport();
  151.             Dimension d = viewport.getViewSize();
  152.  
  153.             int vWidth = d.width;
  154.             int vHeight = d.height;
  155.  
  156.             int w = getWidth();
  157.             int h = getHeight();
  158.  
  159.             float xMult = (float) vWidth / w;
  160.             float yMult = (float) vHeight / h;
  161.  
  162.             int newX = (int) (r.x * xMult);
  163.             int newY = (int) (r.y * yMult);
  164.             int newW = (int) (r.width * xMult);
  165.             int newH = (int) (r.height * yMult);
  166.  
  167.             Rectangle newRect = new Rectangle(newX, newY, newW, newH);
  168.  
  169.             ((JComponent) viewport.getView()).scrollRectToVisible(newRect);
  170.  
  171.             isAdjusting = false;
  172.         }
  173.     }
  174.  
  175.     public void componentShown(ComponentEvent e) {
  176.     }
  177.  
  178.     public void componentHidden(ComponentEvent e) {
  179.     }
  180.  
  181.     public void adjustmentValueChanged(AdjustmentEvent e) {
  182.         if(!isAdjusting) {
  183.             updateOverBox();
  184.         }
  185.     }
  186.  
  187.     /**
  188.      * @param args
  189.      */
  190.     public static void main(String[] args) {
  191.         SwingUtilities.invokeLater(new Runnable() {
  192.             @Override
  193.             public void run() {
  194.                 JScrollPane jsp = new JScrollPane();
  195.                 JTextPane blueEditorPane = new JTextPane();
  196.  
  197.                 StringBuffer buffer = new StringBuffer();
  198.                 for(int i = 0; i < 10; i++) {
  199.                     for(int j = 0; j < 4; ++j) {
  200.                         buffer.append("test" + i + ", " + j);
  201.                     }
  202.  
  203.                     if(i == 5) {
  204.                         for(int j = 0; j < 5; ++j) {
  205.                             buffer.append("\n");
  206.                         }
  207.                     }
  208.                     buffer.append("\n");
  209.                 }
  210.                 blueEditorPane.setText(buffer.toString());
  211.  
  212.                 jsp.setViewportView(blueEditorPane);
  213.                 /*jsp.setViewportView(new JPanel() {
  214.                     {
  215.                         setBackground(Color.GREEN);
  216.                         setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
  217.                     }
  218.                 });*/
  219.  
  220.                 JScrollNavigator nav = new JScrollNavigator();
  221.                 nav.setJScrollPane(jsp);
  222.  
  223.                 JFrame f = new JFrame();
  224.                 JMenuBar bar = new JMenuBar();
  225.                 bar.add(new JMenu("File"));
  226.                 f.setJMenuBar(bar);
  227.  
  228.                 f.getContentPane().add(jsp);
  229.                 nav.setPreferredSize(new Dimension(100, 100));
  230.                 f.getContentPane().add(nav, BorderLayout.WEST);
  231.                 f.setSize(300, 200);
  232.                 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  233.                 f.setVisible(true);
  234.             }
  235.         });
  236.     }
  237.  
  238.  
  239.     class NavBox extends JPanel {
  240.         boolean dragging = false;
  241.  
  242.         Point origin = null;
  243.  
  244.         int originX = -1;
  245.  
  246.         int originY = -1;
  247.  
  248.         public NavBox() {
  249.             this.setBorder(new LineBorder(Color.GREEN, 1));
  250.  
  251.             this.addMouseListener(new MouseAdapter() {
  252.  
  253.                 public void mousePressed(MouseEvent e) {
  254.                     origin = SwingUtilities.convertPoint(NavBox.this, e.getPoint(), NavBox.this.getParent());
  255.                     originX = NavBox.this.getX();
  256.                     originY = NavBox.this.getY();
  257.                 }
  258.  
  259.                 public void mouseReleased(MouseEvent e) {
  260.                     origin = null;
  261.                     originX = -1;
  262.                     originY = -1;
  263.                 }
  264.  
  265.             });
  266.  
  267.             this.addMouseMotionListener(new MouseMotionAdapter() {
  268.  
  269.                 public void mouseDragged(MouseEvent e) {
  270.                     NavBox box = NavBox.this;
  271.                     Container c = box.getParent();
  272.  
  273.                     int leftBound = -originX;
  274.                     int rightBound = c.getWidth() - box.getWidth() - originX;
  275.                     int topBound = -originY;
  276.                     int bottomBound = c.getHeight() - box.getHeight() - originY;
  277.  
  278.                     Point p = SwingUtilities.convertPoint(box, e.getPoint(), c);
  279.  
  280.                     int xDiff = p.x - origin.x;
  281.                     int yDiff = p.y - origin.y;
  282.  
  283.                     if(xDiff < leftBound) {
  284.                         xDiff = leftBound;
  285.                     }
  286.  
  287.                     if(xDiff > rightBound) {
  288.                         xDiff = rightBound;
  289.                     }
  290.  
  291.                     if(yDiff < topBound) {
  292.                         yDiff = topBound;
  293.                     }
  294.  
  295.                     if(yDiff > bottomBound) {
  296.                         yDiff = bottomBound;
  297.                     }
  298.  
  299.                     box.setLocation(originX + xDiff, originY + yDiff);
  300.                    
  301.                     System.out.println("Drag");
  302.                 }
  303.  
  304.             });
  305.         }
  306.     }
  307.  
  308. }