Here is a working example, but you will need java-image-scaling library (its LGPL) - it is used for smooth scaling of preview image, otherwise you get totally ruined image:
public static void main ( String[] args )
{
final JFrame mainFrame = new JFrame ();
JPanel content = new JPanel ( new GridLayout ( 20, 20 ) );
for ( int i = 0; i < 20; i++ )
{
for ( int j = 0; j < 20; j++ )
{
content.add ( new JLabel ( "Test " + i + ":" + j )
{
{
setBorder ( BorderFactory.createEmptyBorder ( 20, 20, 20, 20 ) );
}
} );
}
}
final JScrollPane pane = new JScrollPane ( content );
pane.setCorner ( JScrollPane.LOWER_TRAILING_CORNER, new JButton ()
{
{
final JButton button = this;
addActionListener ( new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
JComponent comp = ( JComponent ) pane.getViewport ().getView ();
Dimension size = comp.getSize ();
Rectangle viewRect = comp.getVisibleRect ();
// Drawing preview
BufferedImage image = new BufferedImage ( size.width, size.height,
BufferedImage.TYPE_INT_RGB );
Graphics2D g2d = image.createGraphics ();
comp.print ( g2d );
g2d.dispose ();
// Rescaling preview
int width = 200;
int height = comp.getHeight () * width / comp.getHeight ();
BufferedImage rescaled =
new ResampleOp ( width, height ).filter ( image, null );
// Drawing view rect
float diff = (float)width / size.width;
g2d = rescaled.createGraphics ();
g2d.setPaint ( Color.RED );
g2d.drawRect ( Math.round ( viewRect.x * diff ),
Math.round ( viewRect.y * diff ),
Math.round ( viewRect.width * diff ),
Math.round ( viewRect.height * diff ) );
g2d.dispose ();
// Displaying preview
final JDialog preview = new JDialog ( mainFrame );
preview.setUndecorated ( true );
preview.add ( new JLabel ( new ImageIcon ( rescaled ) )
{
{
setBorder ( BorderFactory.createLineBorder ( Color.BLACK ) );
setFocusable ( true );
}
} );
Point los = button.getLocationOnScreen ();
preview.setSize ( width + 2, height + 2 );
preview.setLocation ( los.x - width - 2, los.y - height - 2 );
preview.setVisible ( true );
preview.requestFocus ();
preview.addFocusListener ( new FocusAdapter ()
{
public void focusLost ( FocusEvent e )
{
preview.dispose ();
}
} );
}
} );
}
} );
mainFrame.add ( pane );
mainFrame.setSize ( 600, 600 );
mainFrame.setLocationRelativeTo ( null );
mainFrame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
mainFrame.setVisible ( true );
}
So basically i create a snapshot of the panel, rescale it and place into the small dialog that opens near the button and closes on focus loss. Snapshot creation might be time-wasting though, but i don't know any better way to create a rendered preview of the whole scrollpane container - you have to paint it onto something to display preview anyway and that means it will spend the same time.
Also you can easily modify the preview component so you can move the visible rect by dragging the RED rect on the preview dialog :)