// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.Timer;
import org.openstreetmap.josm.tools.ImageProvider;
/** A viewport with UP and DOWN arrow buttons, so that the user can make the
* content scroll.
*/
public class ScrollViewport extends JPanel {
private static final int NO_SCROLL = 0;
public static final int UP_DIRECTION = 1;
public static final int DOWN_DIRECTION = 2;
public static final int LEFT_DIRECTION = 4;
public static final int RIGHT_DIRECTION = 8;
public static final int VERTICAL_DIRECTION = UP_DIRECTION | DOWN_DIRECTION;
public static final int HORIZONTAL_DIRECTION = LEFT_DIRECTION | RIGHT_DIRECTION;
public static final int ALL_DIRECTION = HORIZONTAL_DIRECTION | VERTICAL_DIRECTION;
private class ScrollViewPortMouseListener extends MouseAdapter {
private final int direction;
ScrollViewPortMouseListener(int direction) {
this.direction = direction;
}
@Override
public void mouseExited(MouseEvent arg0) {
ScrollViewport.this.scrollDirection = NO_SCROLL;
timer.stop();
}
@Override
public void mouseReleased(MouseEvent arg0) {
ScrollViewport.this.scrollDirection = NO_SCROLL;
timer.stop();
}
@Override public void mousePressed(MouseEvent arg0) {
ScrollViewport.this.scrollDirection = direction;
scroll();
timer.restart();
}
}
private final JViewport vp = new JViewport();
private JComponent component;
private final List<JButton> buttons = new ArrayList<>();
private final Timer timer = new Timer(100, evt -> scroll());
private int scrollDirection = NO_SCROLL;
public ScrollViewport(JComponent c, int direction) {
this(direction);
add(c);
}
public ScrollViewport(int direction) {
setLayout(new BorderLayout());
JButton button;
// UP
if ((direction & UP_DIRECTION) != 0) {
button = new JButton();
button.addMouseListener(new ScrollViewPortMouseListener(UP_DIRECTION));
button.setPreferredSize(new Dimension(10, 10));
button.setIcon(ImageProvider.get("svpUp"));
add(button, BorderLayout.NORTH);
buttons.add(button);
}
// DOWN
if ((direction & DOWN_DIRECTION) != 0) {
button = new JButton();
button.addMouseListener(new ScrollViewPortMouseListener(DOWN_DIRECTION));
button.setPreferredSize(new Dimension(10, 10));
button.setIcon(ImageProvider.get("svpDown"));
add(button, BorderLayout.SOUTH);
buttons.add(button);
}
// LEFT
if ((direction & LEFT_DIRECTION) != 0) {
button = new JButton();
button.addMouseListener(new ScrollViewPortMouseListener(LEFT_DIRECTION));
button.setPreferredSize(new Dimension(10, 10));
button.setIcon(ImageProvider.get("svpLeft"));
add(button, BorderLayout.WEST);
buttons.add(button);
}
// RIGHT
if ((direction & RIGHT_DIRECTION) != 0) {
button = new JButton();
button.addMouseListener(new ScrollViewPortMouseListener(RIGHT_DIRECTION));
button.setPreferredSize(new Dimension(10, 10));
button.setIcon(ImageProvider.get("svpRight"));
add(button, BorderLayout.EAST);
buttons.add(button);
}
add(vp, BorderLayout.CENTER);
this.addComponentListener(new ComponentAdapter() {
@Override public void componentResized(ComponentEvent e) {
showOrHideButtons();
}
});
showOrHideButtons();
timer.setRepeats(true);
timer.setInitialDelay(400);
}
public synchronized void scroll() {
int direction = scrollDirection;
if (component == null || direction == NO_SCROLL)
return;
Rectangle viewRect = vp.getViewRect();
int deltaX = 0;
int deltaY = 0;
if (direction < LEFT_DIRECTION) {
deltaY = viewRect.height * 2 / 7;
} else {
deltaX = viewRect.width * 2 / 7;
}
switch (direction) {
case UP_DIRECTION :
deltaY *= -1;
break;
case LEFT_DIRECTION :
deltaX *= -1;
break;
default: // Do nothing
}
scroll(deltaX, deltaY);
}
public synchronized void scroll(int deltaX, int deltaY) {
if (component == null)
return;
Dimension compSize = component.getSize();
Rectangle viewRect = vp.getViewRect();
int newX = viewRect.x + deltaX;
int newY = viewRect.y + deltaY;
if (newY < 0) {
newY = 0;
}
if (newY > compSize.height - viewRect.height) {
newY = compSize.height - viewRect.height;
}
if (newX < 0) {
newX = 0;
}
if (newX > compSize.width - viewRect.width) {
newX = compSize.width - viewRect.width;
}
vp.setViewPosition(new Point(newX, newY));
}
/**
* Update the visibility of the buttons
* Only show them if the Viewport is too small for the content.
*/
public void showOrHideButtons() {
boolean needButtons = vp.getViewSize().height > vp.getViewRect().height ||
vp.getViewSize().width > vp.getViewRect().width;
for (JButton b : buttons) {
b.setVisible(needButtons);
}
}
public Rectangle getViewRect() {
return vp.getViewRect();
}
public Dimension getViewSize() {
return vp.getViewSize();
}
public Point getViewPosition() {
return vp.getViewPosition();
}
public void add(JComponent c) {
vp.removeAll();
this.component = c;
vp.add(c);
}
}