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