[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[1180] | 2 | package org.openstreetmap.josm.gui;
|
---|
| 3 |
|
---|
| 4 | import java.awt.BorderLayout;
|
---|
| 5 | import java.awt.Dimension;
|
---|
| 6 | import java.awt.Point;
|
---|
| 7 | import java.awt.Rectangle;
|
---|
| 8 | import java.awt.event.ActionEvent;
|
---|
| 9 | import java.awt.event.ActionListener;
|
---|
[2391] | 10 | import java.awt.event.ComponentAdapter;
|
---|
| 11 | import java.awt.event.ComponentEvent;
|
---|
[1180] | 12 | import java.awt.event.MouseAdapter;
|
---|
| 13 | import java.awt.event.MouseEvent;
|
---|
[2391] | 14 | import java.util.ArrayList;
|
---|
| 15 | import java.util.List;
|
---|
| 16 |
|
---|
[1180] | 17 | import javax.swing.JButton;
|
---|
| 18 | import javax.swing.JComponent;
|
---|
| 19 | import javax.swing.JPanel;
|
---|
| 20 | import javax.swing.JViewport;
|
---|
| 21 | import javax.swing.Timer;
|
---|
| 22 |
|
---|
| 23 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
| 24 |
|
---|
| 25 | /** A viewport with UP and DOWN arrow buttons, so that the user can make the
|
---|
| 26 | * content scroll.
|
---|
| 27 | */
|
---|
| 28 | public class ScrollViewport extends JPanel {
|
---|
| 29 |
|
---|
| 30 | private static final int NO_SCROLL = 0;
|
---|
| 31 |
|
---|
| 32 | public static final int UP_DIRECTION = 1;
|
---|
| 33 | public static final int DOWN_DIRECTION = 2;
|
---|
| 34 | public static final int LEFT_DIRECTION = 4;
|
---|
| 35 | public static final int RIGHT_DIRECTION = 8;
|
---|
| 36 | public static final int VERTICAL_DIRECTION = UP_DIRECTION | DOWN_DIRECTION;
|
---|
| 37 | public static final int HORIZONTAL_DIRECTION = LEFT_DIRECTION | RIGHT_DIRECTION;
|
---|
| 38 | public static final int ALL_DIRECTION = HORIZONTAL_DIRECTION | VERTICAL_DIRECTION;
|
---|
| 39 |
|
---|
| 40 | private class ScrollViewPortMouseListener extends MouseAdapter {
|
---|
[9078] | 41 | private final int direction;
|
---|
[1180] | 42 |
|
---|
[8836] | 43 | ScrollViewPortMouseListener(int direction) {
|
---|
[1180] | 44 | this.direction = direction;
|
---|
| 45 | }
|
---|
| 46 |
|
---|
[8836] | 47 | @Override
|
---|
| 48 | public void mouseExited(MouseEvent arg0) {
|
---|
[1180] | 49 | ScrollViewport.this.scrollDirection = NO_SCROLL;
|
---|
| 50 | timer.stop();
|
---|
| 51 | }
|
---|
| 52 |
|
---|
[8836] | 53 | @Override
|
---|
| 54 | public void mouseReleased(MouseEvent arg0) {
|
---|
[1180] | 55 | ScrollViewport.this.scrollDirection = NO_SCROLL;
|
---|
| 56 | timer.stop();
|
---|
| 57 | }
|
---|
| 58 |
|
---|
| 59 | @Override public void mousePressed(MouseEvent arg0) {
|
---|
| 60 | ScrollViewport.this.scrollDirection = direction;
|
---|
| 61 | scroll();
|
---|
| 62 | timer.restart();
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | }
|
---|
| 66 |
|
---|
[9078] | 67 | private final JViewport vp = new JViewport();
|
---|
[8840] | 68 | private JComponent component;
|
---|
[2512] | 69 |
|
---|
[9078] | 70 | private final List<JButton> buttons = new ArrayList<>();
|
---|
[1180] | 71 |
|
---|
[9078] | 72 | private final Timer timer = new Timer(100, new ActionListener() {
|
---|
[6084] | 73 | @Override
|
---|
[1180] | 74 | public void actionPerformed(ActionEvent arg0) {
|
---|
| 75 | ScrollViewport.this.scroll();
|
---|
| 76 | }
|
---|
| 77 | });
|
---|
| 78 |
|
---|
| 79 | private int scrollDirection = NO_SCROLL;
|
---|
| 80 |
|
---|
| 81 | public ScrollViewport(JComponent c, int direction) {
|
---|
| 82 | this(direction);
|
---|
| 83 | add(c);
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | public ScrollViewport(int direction) {
|
---|
| 87 | setLayout(new BorderLayout());
|
---|
[2512] | 88 |
|
---|
[1180] | 89 | JButton button;
|
---|
| 90 |
|
---|
| 91 | // UP
|
---|
[10244] | 92 | if ((direction & UP_DIRECTION) != 0) {
|
---|
[1180] | 93 | button = new JButton();
|
---|
| 94 | button.addMouseListener(new ScrollViewPortMouseListener(UP_DIRECTION));
|
---|
[8510] | 95 | button.setPreferredSize(new Dimension(10, 10));
|
---|
[1180] | 96 | button.setIcon(ImageProvider.get("svpUp"));
|
---|
| 97 | add(button, BorderLayout.NORTH);
|
---|
[2391] | 98 | buttons.add(button);
|
---|
[1180] | 99 | }
|
---|
| 100 |
|
---|
| 101 | // DOWN
|
---|
[10244] | 102 | if ((direction & DOWN_DIRECTION) != 0) {
|
---|
[1180] | 103 | button = new JButton();
|
---|
| 104 | button.addMouseListener(new ScrollViewPortMouseListener(DOWN_DIRECTION));
|
---|
[8510] | 105 | button.setPreferredSize(new Dimension(10, 10));
|
---|
[1180] | 106 | button.setIcon(ImageProvider.get("svpDown"));
|
---|
| 107 | add(button, BorderLayout.SOUTH);
|
---|
[2391] | 108 | buttons.add(button);
|
---|
[1180] | 109 | }
|
---|
| 110 |
|
---|
| 111 | // LEFT
|
---|
[10244] | 112 | if ((direction & LEFT_DIRECTION) != 0) {
|
---|
[1180] | 113 | button = new JButton();
|
---|
| 114 | button.addMouseListener(new ScrollViewPortMouseListener(LEFT_DIRECTION));
|
---|
[8510] | 115 | button.setPreferredSize(new Dimension(10, 10));
|
---|
[1180] | 116 | button.setIcon(ImageProvider.get("svpLeft"));
|
---|
| 117 | add(button, BorderLayout.WEST);
|
---|
[2391] | 118 | buttons.add(button);
|
---|
[1180] | 119 | }
|
---|
| 120 |
|
---|
| 121 | // RIGHT
|
---|
[10244] | 122 | if ((direction & RIGHT_DIRECTION) != 0) {
|
---|
[1180] | 123 | button = new JButton();
|
---|
| 124 | button.addMouseListener(new ScrollViewPortMouseListener(RIGHT_DIRECTION));
|
---|
[8510] | 125 | button.setPreferredSize(new Dimension(10, 10));
|
---|
[1180] | 126 | button.setIcon(ImageProvider.get("svpRight"));
|
---|
| 127 | add(button, BorderLayout.EAST);
|
---|
[2391] | 128 | buttons.add(button);
|
---|
[1180] | 129 | }
|
---|
| 130 |
|
---|
| 131 | add(vp, BorderLayout.CENTER);
|
---|
| 132 |
|
---|
[2391] | 133 | this.addComponentListener(new ComponentAdapter() {
|
---|
[10378] | 134 | @Override public void componentResized(ComponentEvent e) {
|
---|
[2391] | 135 | showOrHideButtons();
|
---|
| 136 | }
|
---|
| 137 | });
|
---|
| 138 |
|
---|
| 139 | showOrHideButtons();
|
---|
[2512] | 140 |
|
---|
[1180] | 141 | timer.setRepeats(true);
|
---|
| 142 | timer.setInitialDelay(400);
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 | public synchronized void scroll() {
|
---|
| 146 | int direction = scrollDirection;
|
---|
| 147 |
|
---|
[2676] | 148 | if (component == null || direction == NO_SCROLL)
|
---|
[1180] | 149 | return;
|
---|
| 150 |
|
---|
| 151 | Rectangle viewRect = vp.getViewRect();
|
---|
| 152 |
|
---|
[2391] | 153 | int deltaX = 0;
|
---|
| 154 | int deltaY = 0;
|
---|
[1180] | 155 |
|
---|
| 156 | if (direction < LEFT_DIRECTION) {
|
---|
[2391] | 157 | deltaY = viewRect.height * 2 / 7;
|
---|
[1180] | 158 | } else {
|
---|
[2391] | 159 | deltaX = viewRect.width * 2 / 7;
|
---|
[1180] | 160 | }
|
---|
| 161 |
|
---|
| 162 | switch (direction) {
|
---|
[2676] | 163 | case UP_DIRECTION :
|
---|
| 164 | deltaY *= -1;
|
---|
| 165 | break;
|
---|
| 166 | case LEFT_DIRECTION :
|
---|
| 167 | deltaX *= -1;
|
---|
| 168 | break;
|
---|
[10217] | 169 | default: // Do nothing
|
---|
[1180] | 170 | }
|
---|
| 171 |
|
---|
[2391] | 172 | scroll(deltaX, deltaY);
|
---|
| 173 | }
|
---|
[8510] | 174 |
|
---|
[2391] | 175 | public synchronized void scroll(int deltaX, int deltaY) {
|
---|
[2676] | 176 | if (component == null)
|
---|
[2391] | 177 | return;
|
---|
| 178 | Dimension compSize = component.getSize();
|
---|
| 179 | Rectangle viewRect = vp.getViewRect();
|
---|
| 180 |
|
---|
| 181 | int newX = viewRect.x + deltaX;
|
---|
| 182 | int newY = viewRect.y + deltaY;
|
---|
| 183 |
|
---|
| 184 | if (newY < 0) {
|
---|
| 185 | newY = 0;
|
---|
| 186 | }
|
---|
| 187 | if (newY > compSize.height - viewRect.height) {
|
---|
| 188 | newY = compSize.height - viewRect.height;
|
---|
| 189 | }
|
---|
| 190 | if (newX < 0) {
|
---|
| 191 | newX = 0;
|
---|
| 192 | }
|
---|
| 193 | if (newX > compSize.width - viewRect.width) {
|
---|
| 194 | newX = compSize.width - viewRect.width;
|
---|
| 195 | }
|
---|
| 196 |
|
---|
[1180] | 197 | vp.setViewPosition(new Point(newX, newY));
|
---|
| 198 | }
|
---|
| 199 |
|
---|
[2391] | 200 | /**
|
---|
| 201 | * Update the visibility of the buttons
|
---|
| 202 | * Only show them if the Viewport is too small for the content.
|
---|
| 203 | */
|
---|
| 204 | public void showOrHideButtons() {
|
---|
[2512] | 205 | boolean needButtons = vp.getViewSize().height > vp.getViewRect().height ||
|
---|
[2676] | 206 | vp.getViewSize().width > vp.getViewRect().width;
|
---|
[2391] | 207 | for (JButton b : buttons) {
|
---|
| 208 | b.setVisible(needButtons);
|
---|
| 209 | }
|
---|
| 210 | }
|
---|
[2512] | 211 |
|
---|
[1180] | 212 | public Rectangle getViewRect() {
|
---|
| 213 | return vp.getViewRect();
|
---|
| 214 | }
|
---|
| 215 |
|
---|
| 216 | public Dimension getViewSize() {
|
---|
| 217 | return vp.getViewSize();
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | public Point getViewPosition() {
|
---|
| 221 | return vp.getViewPosition();
|
---|
| 222 | }
|
---|
| 223 |
|
---|
| 224 | public void add(JComponent c) {
|
---|
| 225 | vp.removeAll();
|
---|
| 226 | this.component = c;
|
---|
| 227 | vp.add(c);
|
---|
| 228 | }
|
---|
| 229 | }
|
---|