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 }