001 // License: GPL. Copyright 2007 by Christian Gallioz (aka khris78)
002
003 package org.openstreetmap.josm.gui;
004
005 import java.awt.BorderLayout;
006 import java.awt.Dimension;
007 import java.awt.Point;
008 import java.awt.Rectangle;
009 import java.awt.event.ActionEvent;
010 import java.awt.event.ActionListener;
011 import java.awt.event.ComponentAdapter;
012 import java.awt.event.ComponentEvent;
013 import java.awt.event.MouseAdapter;
014 import java.awt.event.MouseEvent;
015 import java.util.ArrayList;
016 import java.util.List;
017
018 import javax.swing.JButton;
019 import javax.swing.JComponent;
020 import javax.swing.JPanel;
021 import javax.swing.JViewport;
022 import javax.swing.Timer;
023
024 import org.openstreetmap.josm.tools.ImageProvider;
025
026 /** A viewport with UP and DOWN arrow buttons, so that the user can make the
027 * content scroll.
028 */
029 public class ScrollViewport extends JPanel {
030
031 private static final int NO_SCROLL = 0;
032
033 public static final int UP_DIRECTION = 1;
034 public static final int DOWN_DIRECTION = 2;
035 public static final int LEFT_DIRECTION = 4;
036 public static final int RIGHT_DIRECTION = 8;
037 public static final int VERTICAL_DIRECTION = UP_DIRECTION | DOWN_DIRECTION;
038 public static final int HORIZONTAL_DIRECTION = LEFT_DIRECTION | RIGHT_DIRECTION;
039 public static final int ALL_DIRECTION = HORIZONTAL_DIRECTION | VERTICAL_DIRECTION;
040
041 private class ScrollViewPortMouseListener extends MouseAdapter {
042 private int direction;
043
044 public ScrollViewPortMouseListener(int direction) {
045 this.direction = direction;
046 }
047
048 @Override public void mouseExited(MouseEvent arg0) {
049 ScrollViewport.this.scrollDirection = NO_SCROLL;
050 timer.stop();
051 }
052
053 @Override public void mouseReleased(MouseEvent arg0) {
054 ScrollViewport.this.scrollDirection = NO_SCROLL;
055 timer.stop();
056 }
057
058 @Override public void mousePressed(MouseEvent arg0) {
059 ScrollViewport.this.scrollDirection = direction;
060 scroll();
061 timer.restart();
062 }
063
064 }
065
066 private JViewport vp = new JViewport();
067 private JComponent component = null;
068
069 private List<JButton> buttons = new ArrayList<JButton>();
070
071 private Timer timer = new Timer(100, new ActionListener() {
072 public void actionPerformed(ActionEvent arg0) {
073 ScrollViewport.this.scroll();
074 }
075 });
076
077 private int scrollDirection = NO_SCROLL;
078
079 public ScrollViewport(JComponent c, int direction) {
080 this(direction);
081 add(c);
082 }
083
084 public ScrollViewport(int direction) {
085 setLayout(new BorderLayout());
086
087 JButton button;
088
089 // UP
090 if ((direction & UP_DIRECTION) > 0) {
091 button = new JButton();
092 button.addMouseListener(new ScrollViewPortMouseListener(UP_DIRECTION));
093 button.setPreferredSize(new Dimension(10,10));
094 button.setIcon(ImageProvider.get("svpUp"));
095 add(button, BorderLayout.NORTH);
096 buttons.add(button);
097 }
098
099 // DOWN
100 if ((direction & DOWN_DIRECTION) > 0) {
101 button = new JButton();
102 button.addMouseListener(new ScrollViewPortMouseListener(DOWN_DIRECTION));
103 button.setPreferredSize(new Dimension(10,10));
104 button.setIcon(ImageProvider.get("svpDown"));
105 add(button, BorderLayout.SOUTH);
106 buttons.add(button);
107 }
108
109 // LEFT
110 if ((direction & LEFT_DIRECTION) > 0) {
111 button = new JButton();
112 button.addMouseListener(new ScrollViewPortMouseListener(LEFT_DIRECTION));
113 button.setPreferredSize(new Dimension(10,10));
114 button.setIcon(ImageProvider.get("svpLeft"));
115 add(button, BorderLayout.WEST);
116 buttons.add(button);
117 }
118
119 // RIGHT
120 if ((direction & RIGHT_DIRECTION) > 0) {
121 button = new JButton();
122 button.addMouseListener(new ScrollViewPortMouseListener(RIGHT_DIRECTION));
123 button.setPreferredSize(new Dimension(10,10));
124 button.setIcon(ImageProvider.get("svpRight"));
125 add(button, BorderLayout.EAST);
126 buttons.add(button);
127 }
128
129 add(vp, BorderLayout.CENTER);
130
131 this.addComponentListener(new ComponentAdapter() {
132 @Override public void componentResized(ComponentEvent e) {
133 showOrHideButtons();
134 }
135 });
136
137 showOrHideButtons();
138
139 timer.setRepeats(true);
140 timer.setInitialDelay(400);
141 }
142
143 public synchronized void scroll() {
144 int direction = scrollDirection;
145
146 if (component == null || direction == NO_SCROLL)
147 return;
148
149 Rectangle viewRect = vp.getViewRect();
150
151 int deltaX = 0;
152 int deltaY = 0;
153
154 if (direction < LEFT_DIRECTION) {
155 deltaY = viewRect.height * 2 / 7;
156 } else {
157 deltaX = viewRect.width * 2 / 7;
158 }
159
160 switch (direction) {
161 case UP_DIRECTION :
162 deltaY *= -1;
163 break;
164 case LEFT_DIRECTION :
165 deltaX *= -1;
166 break;
167 }
168
169 scroll(deltaX, deltaY);
170 }
171 public synchronized void scroll(int deltaX, int deltaY) {
172 if (component == null)
173 return;
174 Dimension compSize = component.getSize();
175 Rectangle viewRect = vp.getViewRect();
176
177 int newX = viewRect.x + deltaX;
178 int newY = viewRect.y + deltaY;
179
180 if (newY < 0) {
181 newY = 0;
182 }
183 if (newY > compSize.height - viewRect.height) {
184 newY = compSize.height - viewRect.height;
185 }
186 if (newX < 0) {
187 newX = 0;
188 }
189 if (newX > compSize.width - viewRect.width) {
190 newX = compSize.width - viewRect.width;
191 }
192
193 vp.setViewPosition(new Point(newX, newY));
194 }
195
196 /**
197 * Update the visibility of the buttons
198 * Only show them if the Viewport is too small for the content.
199 */
200 public void showOrHideButtons() {
201 boolean needButtons = vp.getViewSize().height > vp.getViewRect().height ||
202 vp.getViewSize().width > vp.getViewRect().width;
203 for (JButton b : buttons) {
204 b.setVisible(needButtons);
205 }
206 }
207
208 public Rectangle getViewRect() {
209 return vp.getViewRect();
210 }
211
212 public Dimension getViewSize() {
213 return vp.getViewSize();
214 }
215
216 public Point getViewPosition() {
217 return vp.getViewPosition();
218 }
219
220 public void add(JComponent c) {
221 vp.removeAll();
222 this.component = c;
223 vp.add(c);
224 }
225 }