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