package gsa.flow.ui.util; import javax.swing.*; import javax.swing.border.LineBorder; import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; public class JScrollNavigator extends JPanel implements ComponentListener, AdjustmentListener { private JScrollPane jScrollPane; private NavBox overBox = new NavBox(); boolean isAdjusting = false; public JScrollNavigator() { this.setSize(new Dimension(80, 100)); setBackground(Color.BLACK); setLayout(null); add(overBox); overBox.setOpaque(false); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { updateOverBox(); } }); overBox.addComponentListener(this); } public void setJScrollPane(JScrollPane jScrollPane) { this.jScrollPane = jScrollPane; Component view = jScrollPane.getViewport().getView(); if(view != null) { view.addComponentListener(this); jScrollPane.getViewport().addComponentListener(this); jScrollPane.getHorizontalScrollBar().addAdjustmentListener(this); jScrollPane.getVerticalScrollBar().addAdjustmentListener(this); updateOverBox(); } } private void updateOverBox() { isAdjusting = true; JViewport viewport = this.jScrollPane.getViewport(); Dimension d = viewport.getViewSize(); Rectangle vRect = viewport.getViewRect(); int vWidth = d.width; int vHeight = d.height; int w = getWidth(); int h = getHeight(); float xMult = (float) w / vWidth; float yMult = (float) h / vHeight; int newX = (int) (vRect.x * xMult); int newY = (int) (vRect.y * yMult); int newW = (int) (vRect.width * xMult); int newH = (int) (vRect.height * yMult); overBox.setLocation(newX, newY); overBox.setSize(newW, newH); isAdjusting = false; } /** * @param args */ public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JScrollPane jsp = new JScrollPane(); JTextPane blueEditorPane = new JTextPane(); StringBuffer buffer = new StringBuffer(); for(int i = 0; i < 10; i++) { for(int j = 0; j < 4; ++j) { buffer.append("test" + i + ", " + j); } if(i == 5) { for(int j = 0; j < 5; ++j) { buffer.append("\n"); } } buffer.append("\n"); } blueEditorPane.setText(buffer.toString()); //jsp.setViewportView(blueEditorPane); jsp.setViewportView(new JPanel() { { setBackground(Color.GREEN); setBorder(BorderFactory.createLineBorder(Color.BLACK, 5)); } }); JScrollNavigator nav = new JScrollNavigator(); nav.setJScrollPane(jsp); JFrame f = new JFrame(); JMenuBar bar = new JMenuBar(); bar.add(new JMenu("File")); f.setJMenuBar(bar); f.getContentPane().add(jsp); nav.setPreferredSize(new Dimension(100, 100)); f.getContentPane().add(nav, BorderLayout.WEST); f.setSize(300, 200); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Component view = jScrollPane.getViewport().getView(); BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Paint JScrollPane view to off-screen image and then scale. // It is this action that causes the display corruption! view.paint(g2d); g2d.drawImage(img, 0, 0, null); Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0); g.drawImage(scaled, 0, 0, null); } public void componentResized(ComponentEvent e) { if(e.getSource() == jScrollPane.getViewport() || e.getSource() == jScrollPane.getViewport().getView()) { updateOverBox(); } } public void componentMoved(ComponentEvent e) { if(e.getSource() == overBox) { isAdjusting = true; Rectangle r = overBox.getBounds(); JViewport viewport = this.jScrollPane.getViewport(); Dimension d = viewport.getViewSize(); int vWidth = d.width; int vHeight = d.height; int w = getWidth(); int h = getHeight(); float xMult = (float) vWidth / w; float yMult = (float) vHeight / h; int newX = (int) (r.x * xMult); int newY = (int) (r.y * yMult); int newW = (int) (r.width * xMult); int newH = (int) (r.height * yMult); Rectangle newRect = new Rectangle(newX, newY, newW, newH); ((JComponent) viewport.getView()).scrollRectToVisible(newRect); isAdjusting = false; } } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } public void adjustmentValueChanged(AdjustmentEvent e) { if(!isAdjusting) { updateOverBox(); } } class NavBox extends JPanel { boolean dragging = false; Point origin = null; int originX = -1; int originY = -1; public NavBox() { this.setBorder(new LineBorder(Color.GREEN, 1)); this.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { origin = SwingUtilities.convertPoint(NavBox.this, e.getPoint(), NavBox.this.getParent()); originX = NavBox.this.getX(); originY = NavBox.this.getY(); } public void mouseReleased(MouseEvent e) { origin = null; originX = -1; originY = -1; } }); this.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { NavBox box = NavBox.this; Container c = box.getParent(); int leftBound = -originX; int rightBound = c.getWidth() - box.getWidth() - originX; int topBound = -originY; int bottomBound = c.getHeight() - box.getHeight() - originY; Point p = SwingUtilities.convertPoint(box, e.getPoint(), c); int xDiff = p.x - origin.x; int yDiff = p.y - origin.y; if(xDiff < leftBound) { xDiff = leftBound; } if(xDiff > rightBound) { xDiff = rightBound; } if(yDiff < topBound) { yDiff = topBound; } if(yDiff > bottomBound) { yDiff = bottomBound; } box.setLocation(originX + xDiff, originY + yDiff); } }); } } }