ScrollCanvas.java
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55: package xeij;
56:
57: import java.awt.*;
58: import java.awt.event.*;
59: import java.awt.geom.*;
60: import java.awt.image.*;
61: import java.util.*;
62: import javax.swing.*;
63: import javax.swing.event.*;
64:
65: public class ScrollCanvas extends JScrollPane implements MouseListener, MouseMotionListener, MouseWheelListener {
66:
67: public static final int MIN_SCALE_SHIFT = -4;
68: public static final int MAX_SCALE_SHIFT = 4;
69:
70:
71: protected BufferedImage canvasImage;
72: protected int canvasWidth;
73: protected int canvasHeight;
74:
75:
76: protected int viewportWidth;
77: protected int viewportHeight;
78: protected int marginX;
79: protected int marginY;
80: protected int scaleShift;
81: protected float scaleFactor;
82: protected int direction;
83: protected int scaledWidth;
84: protected int scaledHeight;
85: protected int rotatedScaledWidth;
86: protected int rotatedScaledHeight;
87: protected int viewWidth;
88: protected int viewHeight;
89: protected int originX;
90: protected int originY;
91:
92:
93: protected ArrayList<MouseListener> mouseListeners;
94: protected ArrayList<MouseMotionListener> mouseMotionListeners;
95: protected ArrayList<MouseWheelListener> mouseWheelListeners;
96: protected boolean dragStarted;
97: protected int pressedX;
98: protected int pressedY;
99:
100:
101: protected ArrayList<ScaleShiftListener> scaleShiftListeners;
102:
103:
104: protected JPanel view;
105:
106:
107:
108:
109:
110: public ScrollCanvas () {
111: this (480, 360);
112: }
113: public ScrollCanvas (int width, int height) {
114: this (new BufferedImage (width, height, BufferedImage.TYPE_INT_ARGB));
115: }
116: @SuppressWarnings ("this-escape") public ScrollCanvas (BufferedImage image) {
117:
118: mouseListeners = new ArrayList<MouseListener> ();
119: mouseMotionListeners = new ArrayList<MouseMotionListener> ();
120: mouseWheelListeners = new ArrayList<MouseWheelListener> ();
121: dragStarted = false;
122: pressedX = 0;
123: pressedY = 0;
124:
125: scaleShiftListeners = new ArrayList<ScaleShiftListener> ();
126:
127: canvasImage = image;
128: canvasWidth = image == null ? 1 : image.getWidth ();
129: canvasHeight = image == null ? 1 : image.getHeight ();
130:
131: viewportWidth = canvasWidth;
132: viewportHeight = canvasHeight;
133: marginX = 0;
134: marginY = 0;
135: scaleShift = 0;
136: scaleFactor = 1.0F;
137: direction = 0;
138: calcScaledSize ();
139: calcViewSize ();
140:
141: view = new JPanel () {
142: public void paintComponent (Graphics g) {
143: super.paintComponent (g);
144: paintView (g);
145: }
146: };
147: view.setOpaque (true);
148: view.setBackground (Color.lightGray);
149: view.setPreferredSize (new Dimension (viewWidth, viewHeight));
150: view.addMouseListener (this);
151: view.addMouseMotionListener (this);
152: view.addMouseWheelListener (this);
153:
154: viewport.setScrollMode (JViewport.BLIT_SCROLL_MODE);
155: viewport.setPreferredSize (new Dimension (viewportWidth, viewportHeight));
156: viewport.setMinimumSize (new Dimension (64, 64));
157: viewport.setView (view);
158: viewport.addChangeListener (new ChangeListener () {
159: public void stateChanged (ChangeEvent e) {
160: int width = viewport.getWidth ();
161: int height = viewport.getHeight ();
162: if (viewportWidth != width || viewportHeight != height) {
163: Point2D p = getCenterPoint ();
164: viewportWidth = width;
165: viewportHeight = height;
166: calcViewSize ();
167: calcAdditionalSize ();
168: view.setPreferredSize (new Dimension (viewWidth, viewHeight));
169: setCenterPoint (p);
170: }
171: }
172: });
173: setWheelScrollingEnabled (false);
174: setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
175: setVerticalScrollBarPolicy (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
176: }
177:
178:
179:
180:
181: protected void paintView (Graphics g) {
182: if (canvasImage != null) {
183: Graphics2D g2 = (Graphics2D) g;
184: AffineTransform saveAT = g2.getTransform ();
185: g2.translate ((double) originX, (double) originY);
186: if (direction == 0) {
187: } else if (direction == 1) {
188: g2.translate ((double) rotatedScaledWidth, 0.0);
189: g2.rotate (Math.PI / 2.0);
190: } else if (direction == 2) {
191: g2.translate ((double) rotatedScaledWidth, (double) rotatedScaledHeight);
192: g2.rotate (Math.PI);
193: } else {
194: g2.translate (0.0, (double) rotatedScaledHeight);
195: g2.rotate (Math.PI * 3.0 / 2.0);
196: }
197: g2.translate ((double) (-originX), (double) (-originY));
198:
199: if (scaleShift == 0) {
200: g2.drawImage (canvasImage, originX, originY, null);
201: } else {
202: g2.setRenderingHint (RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
203: g2.drawImage (canvasImage, originX, originY, scaledWidth, scaledHeight, null);
204: }
205:
206: g2.setTransform (saveAT);
207: }
208: }
209:
210:
211: @Override public void addMouseListener (MouseListener ml) {
212: if (ml != null && !mouseListeners.contains (ml)) {
213: mouseListeners.add (ml);
214: }
215: }
216: @Override public void removeMouseListener (MouseListener ml) {
217: mouseListeners.remove (ml);
218: }
219: @Override public MouseListener[] getMouseListeners () {
220: return mouseListeners.toArray (new MouseListener[mouseListeners.size ()]);
221: }
222:
223: @Override public void addMouseMotionListener (MouseMotionListener mml) {
224: if (mml != null && !mouseMotionListeners.contains (mml)) {
225: mouseMotionListeners.add (mml);
226: }
227: }
228: @Override public void removeMouseMotionListener (MouseMotionListener mml) {
229: mouseMotionListeners.remove (mml);
230: }
231: @Override public MouseMotionListener[] getMouseMotionListeners () {
232: return mouseMotionListeners.toArray (new MouseMotionListener[mouseMotionListeners.size ()]);
233: }
234:
235: @Override public void addMouseWheelListener (MouseWheelListener mml) {
236: if (mouseWheelListeners != null) {
237: if (mml != null && !mouseWheelListeners.contains (mml)) {
238: mouseWheelListeners.add (mml);
239: }
240: }
241: }
242: @Override public void removeMouseWheelListener (MouseWheelListener mml) {
243: mouseWheelListeners.remove (mml);
244: }
245: @Override public MouseWheelListener[] getMouseWheelListeners () {
246: return mouseWheelListeners.toArray (new MouseWheelListener[mouseWheelListeners.size ()]);
247: }
248:
249:
250:
251:
252:
253:
254: @Override public void mouseClicked (MouseEvent me) {
255: MouseEvent2D me2D = adjustMouseEvent (me);
256: for (MouseListener ml : mouseListeners) {
257: ml.mouseClicked (me2D);
258: }
259: if (!me2D.isConsumed ()) {
260: if (isFocusable () && !isFocusOwner ()) {
261: requestFocus ();
262: }
263: }
264: }
265: @Override public void mouseEntered (MouseEvent me) {
266: MouseEvent2D me2D = adjustMouseEvent (me);
267: for (MouseListener ml : mouseListeners) {
268: ml.mouseEntered (me2D);
269: }
270: }
271: @Override public void mouseExited (MouseEvent me) {
272: MouseEvent2D me2D = adjustMouseEvent (me);
273: for (MouseListener ml : mouseListeners) {
274: ml.mouseExited (me2D);
275: }
276: }
277: @Override public void mousePressed (MouseEvent me) {
278: int x = me.getX ();
279: int y = me.getY ();
280: MouseEvent2D me2D = adjustMouseEvent (me);
281: for (MouseListener ml : mouseListeners) {
282: ml.mousePressed (me2D);
283: }
284: if (!me2D.isConsumed ()) {
285: dragStarted = true;
286: pressedX = x;
287: pressedY = y;
288: }
289: }
290: @Override public void mouseReleased (MouseEvent me) {
291: MouseEvent2D me2D = adjustMouseEvent (me);
292: for (MouseListener ml : mouseListeners) {
293: ml.mouseReleased (me2D);
294: }
295: if (!me2D.isConsumed ()) {
296: dragStarted = false;
297: }
298: }
299:
300:
301:
302:
303:
304: @Override public void mouseDragged (MouseEvent me) {
305: int x = me.getX ();
306: int y = me.getY ();
307: MouseEvent2D me2D = adjustMouseEvent (me);
308: for (MouseMotionListener ml : mouseMotionListeners) {
309: ml.mouseDragged (me2D);
310: }
311: if (!me2D.isConsumed ()) {
312: if (dragStarted) {
313: Point p = viewport.getViewPosition ();
314: updateViewPosition (p.x - (x - pressedX), p.y - (y - pressedY));
315: }
316: }
317: }
318: @Override public void mouseMoved (MouseEvent me) {
319: MouseEvent2D me2D = adjustMouseEvent (me);
320: for (MouseMotionListener ml : mouseMotionListeners) {
321: ml.mouseMoved (me2D);
322: }
323: }
324:
325:
326:
327:
328: @Override public void mouseWheelMoved (MouseWheelEvent mwe) {
329: MouseWheelEvent2D mwe2D = adjustMouseWheelEvent (mwe);
330: for (MouseWheelListener mwl : mouseWheelListeners) {
331: mwl.mouseWheelMoved (mwe2D);
332: }
333: if (!mwe2D.isConsumed ()) {
334: int n = mwe2D.getWheelRotation ();
335: if (n < 0) {
336: setScaleShift (scaleShift + 1, mwe2D.getPoint2D ());
337: } else if (n > 0) {
338: setScaleShift (scaleShift - 1, mwe2D.getPoint2D ());
339: }
340: }
341: }
342:
343:
344:
345: public int getCanvasWidth () {
346: return canvasWidth;
347: }
348:
349:
350:
351: public int getCanvasHeight () {
352: return canvasHeight;
353: }
354:
355:
356:
357: public BufferedImage getImage () {
358: return canvasImage;
359: }
360:
361:
362:
363: public void setImage (BufferedImage image) {
364: canvasImage = image;
365: int width = image == null ? 1 : image.getWidth ();
366: int height = image == null ? 1 : image.getHeight ();
367: if (width != canvasWidth || height != canvasHeight) {
368: canvasWidth = width;
369: canvasHeight = height;
370: updateView ();
371: } else {
372: view.repaint ();
373: }
374: }
375:
376:
377:
378: public int getMarginX () {
379: return marginX;
380: }
381:
382:
383:
384: public int getMarginY () {
385: return marginY;
386: }
387:
388:
389:
390: public void setMargin (int x, int y) {
391: if (marginX != x || marginY != y) {
392: marginX = x;
393: marginY = y;
394: updateView ();
395: }
396: }
397:
398:
399:
400: public Color getMatColor () {
401: return view.getBackground ();
402: }
403:
404:
405:
406: public void setMatColor (Color color) {
407: view.setBackground (color);
408: view.repaint ();
409: }
410:
411:
412:
413: public int getScaleShift () {
414: return scaleShift;
415: }
416:
417:
418:
419: public void setScaleShift (int shift) {
420: setScaleShift (shift, getCenterPoint ());
421: }
422: public void setScaleShift (int shift, Point2D p) {
423: shift = Math.max (MIN_SCALE_SHIFT, Math.min (MAX_SCALE_SHIFT, shift));
424: if (scaleShift != shift) {
425: Point2D c = getCenterPoint ();
426: double dx = (c.getX () - p.getX ()) * scaleFactor;
427: double dy = (c.getY () - p.getY ()) * scaleFactor;
428: scaleShift = shift;
429: scaleFactor = shift >= 0 ? (float) (1 << shift) : 1.0F / (float) (1 << -shift);
430: updateView ();
431: setCenterPoint (new Point2D.Double (p.getX () + dx / scaleFactor, p.getY () + dy / scaleFactor));
432: for (ScaleShiftListener listener : scaleShiftListeners) {
433: listener.scaleShiftChanged (scaleShift);
434: }
435: }
436: }
437:
438:
439: public static class ScaleShiftListener {
440: public void scaleShiftChanged (int scaleShift) {
441: }
442: }
443: public void addScaleShiftListener (ScaleShiftListener listener) {
444: if (listener != null) {
445: scaleShiftListeners.add (listener);
446: }
447: }
448: public void removeScaleShiftListener (ScaleShiftListener listener) {
449: scaleShiftListeners.remove (listener);
450: }
451: public ScaleShiftListener[] getScaleShiftListeners () {
452: return scaleShiftListeners.toArray (new ScaleShiftListener[scaleShiftListeners.size ()]);
453: }
454:
455:
456:
457: public int getDirection () {
458: return direction;
459: }
460:
461:
462:
463: public void setDirection (int direction) {
464: this.direction = direction;
465: updateView ();
466: }
467:
468:
469:
470: public Point2D getCenterPoint () {
471: Point p = viewport.getViewPosition ();
472: return new Point2D.Float ((p.x + (viewportWidth >> 1) - originX) / scaleFactor,
473: (p.y + (viewportHeight >> 1) - originY) / scaleFactor);
474: }
475:
476:
477:
478: public void setCenterPoint (Point2D p) {
479: updateViewPosition ((int) (p.getX () * scaleFactor) + originX - (viewportWidth >> 1),
480: (int) (p.getY () * scaleFactor) + originY - (viewportHeight >> 1));
481: }
482:
483:
484:
485:
486:
487:
488:
489: protected MouseEvent2D adjustMouseEvent (MouseEvent me) {
490: return new MouseEvent2D (this, me.getID (), me.getWhen (), me.getModifiersEx (),
491: (float) (me.getX () - originX) / scaleFactor,
492: (float) (me.getY () - originY) / scaleFactor,
493: me.getClickCount (), me.isPopupTrigger (),
494: me.getButton ());
495: }
496: protected MouseWheelEvent2D adjustMouseWheelEvent (MouseWheelEvent mwe) {
497: return new MouseWheelEvent2D (this, mwe.getID (), mwe.getWhen (), mwe.getModifiersEx (),
498: (float) (mwe.getX () - originX) / scaleFactor,
499: (float) (mwe.getY () - originY) / scaleFactor,
500: mwe.getClickCount (), mwe.isPopupTrigger (),
501: mwe.getScrollType (), mwe.getScrollAmount (), mwe.getWheelRotation ());
502: }
503:
504:
505:
506: protected void updateView () {
507: calcScaledSize ();
508: calcViewSize ();
509: calcAdditionalSize ();
510: Dimension d = new Dimension (viewWidth, viewHeight);
511: view.setPreferredSize (d);
512:
513:
514: viewport.setViewSize (d);
515: view.repaint ();
516: }
517:
518:
519:
520: protected final void calcScaledSize () {
521: if (scaleShift >= 0) {
522: scaledWidth = canvasWidth << scaleShift;
523: scaledHeight = canvasHeight << scaleShift;
524: } else {
525: scaledWidth = canvasWidth >> -scaleShift;
526: scaledHeight = canvasHeight >> -scaleShift;
527: if (scaledWidth < 1) {
528: scaledWidth = 1;
529: }
530: if (scaledHeight < 1) {
531: scaledHeight = 1;
532: }
533: }
534: if ((direction & 1) == 0) {
535: rotatedScaledWidth = scaledWidth;
536: rotatedScaledHeight = scaledHeight;
537: } else {
538: rotatedScaledWidth = scaledHeight;
539: rotatedScaledHeight = scaledWidth;
540: }
541: }
542:
543:
544:
545: protected final void calcViewSize () {
546: if (viewportWidth < (marginX << 1) + rotatedScaledWidth) {
547: viewWidth = (marginX << 1) + rotatedScaledWidth;
548: originX = marginX;
549: } else {
550: viewWidth = viewportWidth;
551: originX = (viewportWidth - rotatedScaledWidth) >> 1;
552: }
553: if (viewportHeight < (marginY << 1) + rotatedScaledHeight) {
554: viewHeight = (marginY << 1) + rotatedScaledHeight;
555: originY = marginY;
556: } else {
557: viewHeight = viewportHeight;
558: originY = (viewportHeight - rotatedScaledHeight) >> 1;
559: }
560: }
561:
562:
563:
564: protected void calcAdditionalSize () {
565: }
566:
567:
568:
569: protected void updateViewPosition (int x, int y) {
570: if (originX > marginX || x < 0) {
571: x = 0;
572: } else if (x > (marginX << 1) + rotatedScaledWidth - viewportWidth) {
573: x = (marginX << 1) + rotatedScaledWidth - viewportWidth;
574: }
575: if (originY > marginY || y < 0) {
576: y = 0;
577: } else if (y > (marginY << 1) + rotatedScaledHeight - viewportHeight) {
578: y = (marginY << 1) + rotatedScaledHeight - viewportHeight;
579: }
580: viewport.setViewPosition (new Point (x, y));
581: }
582:
583:
584:
585: @Override public void repaint () {
586: super.repaint ();
587: if (view != null) {
588: view.repaint ();
589: }
590: }
591:
592: }
593:
594:
595: