package com.jidesoft.swing;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
/**
* Original code http://forums.java.net/jive/thread.jspa?forumID=73&threadID=14674 under "Do whatever you want with this
* code" license
*/
@SuppressWarnings("serial")
public class ScrollPaneOverview extends JComponent {
private static final int MAX_SIZE = 400;
private static final int MAX_SCALE = 20;
private Component _owner;
private JScrollPane _scrollPane;
private Component _viewComponent;
protected JPopupMenu _popupMenu;
private BufferedImage _image;
private Rectangle _startRectangle;
private Rectangle _rectangle;
private Point _startPoint;
private double _scale;
private int xOffset;
private int yOffset;
private Color _selectionBorder = Color.BLACK;
public ScrollPaneOverview(JScrollPane scrollPane, Component owner) {
_scrollPane = scrollPane;
_owner = owner;
_image = null;
_startRectangle = null;
_rectangle = null;
_startPoint = null;
_scale = 0.0;
setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
MouseInputListener mil = new MouseInputAdapter() {
// A new approach suggested by thomas.bierhance@sdm.de
@Override
public void mousePressed(MouseEvent e) {
if (_startPoint != null) {
Point newPoint = e.getPoint();
int deltaX = (int) ((newPoint.x - _startPoint.x) / _scale);
int deltaY = (int) ((newPoint.y - _startPoint.y) / _scale);
scroll(deltaX, deltaY);
}
_startPoint = null;
_startRectangle = _rectangle;
}
@Override
public void mouseMoved(MouseEvent e) {
if (_startPoint == null) {
_startPoint = new Point(_rectangle.x + _rectangle.width / 2, _rectangle.y + _rectangle.height / 2);
}
Point newPoint = e.getPoint();
moveRectangle(newPoint.x - _startPoint.x, newPoint.y - _startPoint.y);
}
};
addMouseListener(mil);
addMouseMotionListener(mil);
_popupMenu = new JPopupMenu();
_popupMenu.setLayout(new BorderLayout());
_popupMenu.add(this, BorderLayout.CENTER);
}
public void setSelectionBorderColor(Color selectionBorder) {
_selectionBorder = selectionBorder;
}
public Color getSelectionBorder() {
return _selectionBorder;
}
@Override
protected void paintComponent(Graphics g) {
if (_image == null || _rectangle == null)
return;
Graphics2D g2d = (Graphics2D) g;
Insets insets = getInsets();
int xOffset = insets.left;
int yOffset = insets.top;
g.setColor(_scrollPane.getViewport().getView().getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(_image, xOffset, yOffset, null);
int availableWidth = getWidth() - insets.left - insets.right;
int availableHeight = getHeight() - insets.top - insets.bottom;
Area area = new Area(new Rectangle(xOffset, yOffset, availableWidth, availableHeight));
area.subtract(new Area(_rectangle));
g.setColor(new Color(255, 255, 255, 128));
g2d.fill(area);
Color oldcolor = g.getColor();
g.setColor(_selectionBorder);
g.drawRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);
g.setColor(oldcolor);
}
@Override
public Dimension getPreferredSize() {
if (_image == null || _rectangle == null)
return new Dimension();
Insets insets = getInsets();
return new Dimension(_image.getWidth(null) + insets.left + insets.right, _image.getHeight(null) + insets.top + insets.bottom);
}
public void display() {
_viewComponent = _scrollPane.getViewport().getView();
if (_viewComponent == null) {
return;
}
int maxSize = Math.max(MAX_SIZE, Math.max(_scrollPane.getWidth(), _scrollPane.getHeight()) / 2);
int width = Math.min(_viewComponent.getWidth(), _scrollPane.getViewport().getWidth() * MAX_SCALE);
if (width <= 0) {
return;
}
int height = Math.min(_viewComponent.getHeight(), _scrollPane.getViewport().getHeight() * MAX_SCALE);
if (height <= 0) {
return;
}
double scaleX = (double) maxSize / width;
double scaleY = (double) maxSize / height;
_scale = Math.max(1.0 / MAX_SCALE, Math.min(scaleX, scaleY));
_image = new BufferedImage((int) (width * _scale), (int) (height * _scale), BufferedImage.TYPE_INT_RGB);
Graphics2D g = _image.createGraphics();
// If the view is larger than the max scale allows only the the top left most part will now be painted
// One solution would be paint only the part around the current position, but I can't get it to paint - Walter Laan.
// note that without limiting the scale, the width/height will become zero (illegal for BufferedImage)
// See CornerScrollerVisualTest in the test folder
// g.setColor(_viewComponent.getBackground());
// g.fillRect(0, 0, _viewComponent.getWidth(), _viewComponent.getHeight());
// Point viewPosition = _scrollPane.getViewport().getViewPosition();
// xOffset = Math.max(0, viewPosition.x - (width / 2));
// yOffset = Math.max(0, viewPosition.y - (height / 2));
// g.translate(-xOffset, -yOffset);
// g.setClip(0, 0, width, height);
g.scale(_scale, _scale);
g.setClip(xOffset, yOffset, width, height);
/// {{{ Qian Qian 10/72007
boolean wasDoubleBuffered = _viewComponent.isDoubleBuffered();
try {
if (_viewComponent instanceof JComponent) {
((JComponent) _viewComponent).setDoubleBuffered(false);
}
_viewComponent.paint(g);
}
finally {
if (_viewComponent instanceof JComponent) {
((JComponent) _viewComponent).setDoubleBuffered(wasDoubleBuffered);
}
g.dispose();
}
/// QianQian 10/7/2007 }}}
_startRectangle = _scrollPane.getViewport().getViewRect();
Insets insets = getInsets();
_startRectangle.x = (int) (_scale * _startRectangle.x + insets.left);
_startRectangle.y = (int) (_scale * _startRectangle.y + insets.right);
_startRectangle.width *= _scale;
_startRectangle.height *= _scale;
_rectangle = _startRectangle;
Point centerPoint = new Point(_rectangle.x + _rectangle.width / 2, _rectangle.y + _rectangle.height / 2);
showPopup(-centerPoint.x, -centerPoint.y, _owner);
}
/**
* Show popup at designated location.
* <p/>
* You could override this method to show the popup in different location.
*
* @param x the x axis pixel
* @param y the y axis pixel
* @param owner the owner of the popup
*/
protected void showPopup(int x, int y, Component owner) {
_popupMenu.show(owner, x, y);
}
private void moveRectangle(int aDeltaX, int aDeltaY) {
if (_startRectangle == null)
return;
Insets insets = getInsets();
Rectangle newRect = new Rectangle(_startRectangle);
newRect.x += aDeltaX;
newRect.y += aDeltaY;
newRect.x = Math.min(Math.max(newRect.x, insets.left), getWidth() - insets.right - newRect.width);
newRect.y = Math.min(Math.max(newRect.y, insets.right), getHeight() - insets.bottom - newRect.height);
Rectangle clip = new Rectangle();
Rectangle.union(_rectangle, newRect, clip);
clip.grow(2, 2);
_rectangle = newRect;
paintImmediately(clip);
}
private void scroll(int aDeltaX, int aDeltaY) {
JComponent component = (JComponent) _scrollPane.getViewport().getView();
Rectangle rect = component.getVisibleRect();
rect.x += xOffset + aDeltaX;
rect.y += yOffset + aDeltaY;
component.scrollRectToVisible(rect);
_popupMenu.setVisible(false);
}
}