1
28 package demo.view.orgchart;
29
30 import y.anim.AnimationObject;
31 import y.anim.AnimationPlayer;
32 import y.util.DefaultMutableValue2D;
33 import y.view.Graph2DView;
34 import y.view.ViewAnimationFactory;
35
36 import javax.swing.Timer;
37 import java.awt.Rectangle;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.awt.event.KeyAdapter;
41 import java.awt.event.KeyEvent;
42 import java.awt.event.KeyListener;
43 import java.awt.geom.Point2D;
44
45
49 public class KeyboardNavigation {
50
51 private Graph2DView view;
52
53
57 public KeyboardNavigation(final Graph2DView view) {
58 this.view = view;
59 }
60
61
65 public KeyListener createZoomInKeyListener(final int keyCode1, final int keyCode2 )
66 {
67 return new ZoomTrigger(true, keyCode1, keyCode2);
68 }
69
70
71
75 public KeyListener createZoomOutKeyListener(final int keyCode1, final int keyCode2 )
76 {
77 return new ZoomTrigger(false, keyCode1, keyCode2);
78 }
79
80
84 public KeyListener createMoveViewportUpKeyListener(final int keyCode)
85 {
86 return new MoveViewportTrigger(MoveViewportTrigger.UP, keyCode);
87 }
88
89
93 public KeyListener createMoveViewportDownKeyListener(final int keyCode)
94 {
95 return new MoveViewportTrigger(MoveViewportTrigger.DOWN, keyCode);
96 }
97
98
99
103 public KeyListener createMoveViewportLeftKeyListener(final int keyCode)
104 {
105 return new MoveViewportTrigger(MoveViewportTrigger.LEFT, keyCode);
106 }
107
108
112 public KeyListener createMoveViewportRightKeyListener(final int keyCode)
113 {
114 return new MoveViewportTrigger(MoveViewportTrigger.RIGHT, keyCode);
115 }
116
117
122 private abstract class AnimationTrigger extends KeyAdapter {
123 private AnimationPlayer player;
124 private DisarmableAnimationWrapper wrapper;
125
126 private final int keyCode1;
127 private final int keyCode2;
128 private final Timer timer;
129
130 AnimationTrigger( final int keyCode1, final int keyCode2 ) {
131 this.keyCode1 = keyCode1;
132 this.keyCode2 = keyCode2;
133 this.timer = new Timer(0, new ActionListener() {
134 public void actionPerformed( final ActionEvent e ) {
135 if (player != null && player.isPlaying()) {
136 wrapper.disarm();
137 player.stop();
138 }
139 }
140 });
141 this.timer.setInitialDelay(50);
142 this.timer.setRepeats(false);
143 }
144
145 public void keyPressed( final KeyEvent e ) {
146 if (player == null) {
147 player = new AnimationPlayer(false);
148 player.addAnimationListener(view);
149 }
150
151 final int keyCode = e.getKeyCode();
152 if ((keyCode1 == keyCode || keyCode2 == keyCode) &&
153 (0 == e.getModifiersEx())) {
154 timer.stop();
155 if (!player.isPlaying()) {
156 wrapper = new DisarmableAnimationWrapper(createAnimation());
157 if (wrapper.preferredDuration() > 0) {
158 player.animate(wrapper);
159 }
160 }
161 }
162 }
163
164 public void keyReleased( final KeyEvent e ) {
165 final int keyCode = e.getKeyCode();
166 if ((keyCode1 == keyCode || keyCode2 == keyCode) &&
167 (0 == e.getModifiersEx())) {
168 if (!timer.isRunning()) {
169 timer.restart();
170 }
171 }
172 }
173
174 abstract AnimationObject createAnimation();
175 }
176
177
180 class ZoomTrigger extends AnimationTrigger {
181 private ViewAnimationFactory factory;
182 private final boolean zoomIn;
183
184 ZoomTrigger( final boolean zoomIn, final int keyCode1, final int keyCode2 ) {
185 super(keyCode1, keyCode2);
186 this.zoomIn = zoomIn;
187 }
188
189 AnimationObject createAnimation() {
190 final double newZoom = calculateZoom();
191 if ((zoomIn && newZoom > view.getZoom()) || (!zoomIn && newZoom < view.getZoom())) {
192 if (factory == null) {
193 factory = new ViewAnimationFactory(view);
194 }
195 return factory.zoom(newZoom, ViewAnimationFactory.APPLY_EFFECT, 1000);
196 } else {
197 return null;
198 }
199 }
200
201 private double calculateZoom() {
202 if (zoomIn) {
203 return 4;
204 } else {
205 final Point2D oldP = view.getViewPoint2D();
206 final double oldZoom = view.getZoom();
207 view.fitContent();
208 final double fitContentZoom = view.getZoom();
209 view.setZoom(oldZoom);
210 view.setViewPoint2D(oldP.getX(), oldP.getY());
211 return fitContentZoom;
212 }
213 }
214 }
215
216
217
220 private class MoveViewportTrigger extends AnimationTrigger {
221 static final int LEFT = 0;
222 static final int RIGHT = 1;
223 static final int UP = 2;
224 static final int DOWN = 3;
225
226 private int direction;
227 private ViewAnimationFactory factory;
228
229 MoveViewportTrigger( final int direction, final int keyCode ) {
230 super(keyCode, -1);
231 this.direction = direction;
232 }
233
234 AnimationObject createAnimation() {
235 final Rectangle bx = view.getGraph2D().getBoundingBox();
236 final double dx = Math.max(bx.getWidth(), 10000);
237 final double dy = Math.max(bx.getHeight(), 10000);
238
239 final Point2D oldCenter = view.getCenter();
240 final DefaultMutableValue2D newCenter =
241 DefaultMutableValue2D.create(oldCenter.getX(), oldCenter.getY());
242
243 double dist = 0;
244 switch (direction) {
245 case LEFT:
246 newCenter.setX(bx.getX() - dx);
247 dist = oldCenter.getX() - newCenter.getX();
248 break;
249 case RIGHT:
250 newCenter.setX(bx.getX() + bx.getWidth() + dx);
251 dist = newCenter.getX() - oldCenter.getX();
252 break;
253 case UP:
254 newCenter.setY(bx.getY() - dy);
255 dist = oldCenter.getY() - newCenter.getY();
256 break;
257 case DOWN:
258 newCenter.setY(bx.getY() + bx.getHeight() + dy);
259 dist = newCenter.getY() - oldCenter.getY();
260 break;
261 }
262
263 if (dist > 1e-4) {
264 if (factory == null) {
265 factory = new ViewAnimationFactory(view);
266 }
267 return factory.moveCamera(newCenter, (long) Math.rint(dist * view.getZoom()));
268 } else {
269 return null;
270 }
271 }
272 }
273
274
277 private static final class DisarmableAnimationWrapper implements AnimationObject {
278 private AnimationObject animation;
279
280 private boolean armed;
281
282 DisarmableAnimationWrapper( final AnimationObject animation ) {
283 this.animation = animation;
284 this.armed = true;
285 }
286
287 public void initAnimation() {
288 if (armed && animation != null) {
289 animation.initAnimation();
290 }
291 }
292
293 public void calcFrame( final double time ) {
294 if (armed && animation != null) {
295 animation.calcFrame(time);
296 }
297 }
298
299 public void disposeAnimation() {
300 if (armed && animation != null) {
301 animation.disposeAnimation();
302 }
303 }
304
305 public long preferredDuration() {
306 return animation != null ? animation.preferredDuration() : 0;
307 }
308
309 void disarm() {
310 armed = false;
311 }
312 }
313
314 }
315