1
14 package demo.view.orgchart;
15
16 import java.awt.Rectangle;
17 import java.awt.event.ActionEvent;
18 import java.awt.event.ActionListener;
19 import java.awt.event.KeyAdapter;
20 import java.awt.event.KeyEvent;
21 import java.awt.event.KeyListener;
22 import java.awt.geom.Point2D;
23
24 import javax.swing.Timer;
25
26 import y.anim.AnimationObject;
27 import y.anim.AnimationPlayer;
28 import y.util.DefaultMutableValue2D;
29 import y.view.Graph2DView;
30 import y.view.ViewAnimationFactory;
31
32
36 public class KeyboardNavigation {
37
38 private Graph2DView view;
39
40
44 public KeyboardNavigation(Graph2DView view) {
45 this.view = view;
46 }
47
48
52 public KeyListener createZoomInKeyListener(final int keyCode1, final int keyCode2 )
53 {
54 return new ZoomTrigger(true, keyCode1, keyCode2);
55 }
56
57
58
62 public KeyListener createZoomOutKeyListener(final int keyCode1, final int keyCode2 )
63 {
64 return new ZoomTrigger(false, keyCode1, keyCode2);
65 }
66
67
71 public KeyListener createMoveViewportUpKeyListener(final int keyCode)
72 {
73 return new MoveViewportTrigger(MoveViewportTrigger.UP, keyCode);
74 }
75
76
80 public KeyListener createMoveViewportDownKeyListener(final int keyCode)
81 {
82 return new MoveViewportTrigger(MoveViewportTrigger.DOWN, keyCode);
83 }
84
85
86
90 public KeyListener createMoveViewportLeftKeyListener(final int keyCode)
91 {
92 return new MoveViewportTrigger(MoveViewportTrigger.LEFT, keyCode);
93 }
94
95
99 public KeyListener createMoveViewportRightKeyListener(final int keyCode)
100 {
101 return new MoveViewportTrigger(MoveViewportTrigger.RIGHT, keyCode);
102 }
103
104
109 private abstract class AnimationTrigger extends KeyAdapter {
110 private AnimationPlayer player;
111 private DisarmableAnimationWrapper wrapper;
112
113 private final int keyCode1;
114 private final int keyCode2;
115 private final Timer timer;
116
117 AnimationTrigger( final int keyCode1, final int keyCode2 ) {
118 this.keyCode1 = keyCode1;
119 this.keyCode2 = keyCode2;
120 this.timer = new Timer(0, new ActionListener() {
121 public void actionPerformed( final ActionEvent e ) {
122 if (player != null && player.isPlaying()) {
123 wrapper.disarm();
124 player.stop();
125 }
126 }
127 });
128 this.timer.setInitialDelay(50);
129 this.timer.setRepeats(false);
130 }
131
132 public void keyPressed( final KeyEvent e ) {
133 if (player == null) {
134 player = new AnimationPlayer(false);
135 player.addAnimationListener(view);
136 }
137
138 final int keyCode = e.getKeyCode();
139 if ((keyCode1 == keyCode || keyCode2 == keyCode) &&
140 (0 == e.getModifiersEx())) {
141 timer.stop();
142 if (!player.isPlaying()) {
143 wrapper = new DisarmableAnimationWrapper(createAnimation());
144 if (wrapper.preferredDuration() > 0) {
145 player.animate(wrapper);
146 }
147 }
148 }
149 }
150
151 public void keyReleased( final KeyEvent e ) {
152 final int keyCode = e.getKeyCode();
153 if ((keyCode1 == keyCode || keyCode2 == keyCode) &&
154 (0 == e.getModifiersEx())) {
155 if (!timer.isRunning()) {
156 timer.restart();
157 }
158 }
159 }
160
161 abstract AnimationObject createAnimation();
162 }
163
164
167 class ZoomTrigger extends AnimationTrigger {
168 private ViewAnimationFactory factory;
169 private final boolean zoomIn;
170
171 ZoomTrigger( final boolean zoomIn, final int keyCode1, final int keyCode2 ) {
172 super(keyCode1, keyCode2);
173 this.zoomIn = zoomIn;
174 }
175
176 AnimationObject createAnimation() {
177 final double newZoom = calculateZoom();
178 if ((zoomIn && newZoom > view.getZoom()) || (!zoomIn && newZoom < view.getZoom())) {
179 if (factory == null) {
180 factory = new ViewAnimationFactory(view);
181 }
182 return factory.zoom(newZoom, ViewAnimationFactory.APPLY_EFFECT, 1000);
183 } else {
184 return null;
185 }
186 }
187
188 private double calculateZoom() {
189 if (zoomIn) {
190 return 4;
191 } else {
192 Point2D oldP = view.getViewPoint2D();
193 double oldZoom = view.getZoom();
194 view.fitContent();
195 double fitContentZoom = view.getZoom();
196 view.setZoom(oldZoom);
197 view.setViewPoint2D(oldP.getX(), oldP.getY());
198 return fitContentZoom;
199 }
200 }
201 }
202
203
204
207 private class MoveViewportTrigger extends AnimationTrigger {
208 static final int LEFT = 0;
209 static final int RIGHT = 1;
210 static final int UP = 2;
211 static final int DOWN = 3;
212
213 private int direction;
214 private ViewAnimationFactory factory;
215
216 MoveViewportTrigger( final int direction, final int keyCode ) {
217 super(keyCode, -1);
218 this.direction = direction;
219 }
220
221 AnimationObject createAnimation() {
222 final Rectangle bx = view.getGraph2D().getBoundingBox();
223 final double dx = Math.max(bx.getWidth(), 10000);
224 final double dy = Math.max(bx.getHeight(), 10000);
225
226 final Point2D oldCenter = view.getCenter();
227 final DefaultMutableValue2D newCenter =
228 DefaultMutableValue2D.create(oldCenter.getX(), oldCenter.getY());
229
230 double dist = 0;
231 switch (direction) {
232 case LEFT:
233 newCenter.setX(bx.getX() - dx);
234 dist = oldCenter.getX() - newCenter.getX();
235 break;
236 case RIGHT:
237 newCenter.setX(bx.getX() + bx.getWidth() + dx);
238 dist = newCenter.getX() - oldCenter.getX();
239 break;
240 case UP:
241 newCenter.setY(bx.getY() - dy);
242 dist = oldCenter.getY() - newCenter.getY();
243 break;
244 case DOWN:
245 newCenter.setY(bx.getY() + bx.getHeight() + dy);
246 dist = newCenter.getY() - oldCenter.getY();
247 break;
248 }
249
250 if (dist > 1e-4) {
251 if (factory == null) {
252 factory = new ViewAnimationFactory(view);
253 }
254 return factory.moveCamera(newCenter, (long) Math.rint(dist * view.getZoom()));
255 } else {
256 return null;
257 }
258 }
259 }
260
261
264 private static final class DisarmableAnimationWrapper implements AnimationObject {
265 private AnimationObject animation;
266
267 private boolean armed;
268
269 DisarmableAnimationWrapper( final AnimationObject animation ) {
270 this.animation = animation;
271 this.armed = true;
272 }
273
274 public void initAnimation() {
275 if (armed && animation != null) {
276 animation.initAnimation();
277 }
278 }
279
280 public void calcFrame( final double time ) {
281 if (armed && animation != null) {
282 animation.calcFrame(time);
283 }
284 }
285
286 public void disposeAnimation() {
287 if (armed && animation != null) {
288 animation.disposeAnimation();
289 }
290 }
291
292 public long preferredDuration() {
293 return animation != null ? animation.preferredDuration() : 0;
294 }
295
296 void disarm() {
297 armed = false;
298 }
299 }
300
301 }
302