import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
public class JScrollNavigator extends JPanel implements ComponentListener, AdjustmentListener {
private JScrollPane jScrollPane;
private boolean blockRepaint = false;
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;
}
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
if(!blockRepaint){
final JComponent view = (JComponent)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);
ImageObserver io = new ImageObserver() {
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y,int width, int height) {
boolean result = true;
System.out.println("Hi" + g.drawImage(img, 0, 0, null));
if((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS){
blockRepaint = false;
result = false;
System.out.println("BlockRemoved");
}if((infoflags & ImageObserver.ABORT) == ImageObserver.ABORT){
System.out.println("ABORT");
}if((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS){
System.out.println("ALLBITS");
}if((infoflags & ImageObserver.ERROR) == ImageObserver.ERROR){
System.out.println("ERROR");
}if((infoflags & ImageObserver.FRAMEBITS) == ImageObserver.FRAMEBITS){
System.out.println("FRAMEBITS");
blockRepaint = false;
result = false;
}if((infoflags & ImageObserver.HEIGHT) == ImageObserver.HEIGHT){
System.out.println("HEIGHT");
}if((infoflags & ImageObserver.PROPERTIES) == ImageObserver.PROPERTIES){
System.out.println("PROPERTIES");
}if((infoflags & ImageObserver.SOMEBITS) == ImageObserver.SOMEBITS){
System.out.println("SOMEBITS");
}if((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH){
System.out.println("WIDTH");
}
return result;
}
};
//System.out.println(g2d.drawImage(img, view.getX(), view.getY(), null));
Image scaled = img.getScaledInstance(getWidth(), getHeight(), java.awt.Image.SCALE_FAST);
blockRepaint = g.drawImage(scaled, 0, 0, io);
System.out.println(blockRepaint);
}
}
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();
}
}
/**
* @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);
}
});
}
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);
System.out.println("Drag");
}
});
}
}
}