001: /*************************************************************************
002: * *
003: * 1) This source code file, in unmodified form, and compiled classes *
004: * derived from it can be used and distributed without restriction, *
005: * including for commercial use. (Attribution is not required *
006: * but is appreciated.) *
007: * *
008: * 2) Modified versions of this file can be made and distributed *
009: * provided: the modified versions are put into a Java package *
010: * different from the original package, edu.hws; modified *
011: * versions are distributed under the same terms as the original; *
012: * and the modifications are documented in comments. (Modification *
013: * here does not include simply making subclasses that belong to *
014: * a package other than edu.hws, which can be done without any *
015: * restriction.) *
016: * *
017: * David J. Eck *
018: * Department of Mathematics and Computer Science *
019: * Hobart and William Smith Colleges *
020: * Geneva, New York 14456, USA *
021: * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ *
022: * *
023: *************************************************************************/package edu.hws.jcm.draw;
024:
025: import java.awt.*;
026: import java.awt.event.*;
027: import edu.hws.jcm.awt.Controller;
028:
029: /**
030: * When a Panner object is added to a CoordinateRect, it becomes possible to
031: * "grab" the coordinate rectangle and pan it (that is, slide it around by
032: * moving it with the mouse). By default, the user must right-click-and-drag
033: * to pan the coordinates, but this can be changed by providing an argument to
034: * the constructor. It is possible to set a Controller to be notified each time
035: * the mouse moves while the user is dragging. Alternatively, or in addition,
036: * you can set a Controller to be notified when the user finishes dragging.
037: * However, for the most part this is unnecessary, since the Drawables in the
038: * CoordinateRect will for the most part redraw themselves properly when the
039: * limits on the CoordinateRect change. However, if you have Computable objects
040: * that depend on the coordinate limits, then they will need to be recomputed.
041: * (This will be the case if you use value objects returned by the
042: * getValueObject() method in the CoordinateRect class.)
043: * <p>A Panner, p, is inactive if its "visible" property has been set to false.
044: * (This is done by calling p.setVisible(false).)
045: */
046: public class Panner extends Drawable implements MouseListener,
047: MouseMotionListener {
048:
049: private int modifiers; // Combination of MouseEvent.SHIFT_MASK, MouseEvent.CTRL_MASK,
050: // MouseEvent.META_MASK, and MouseEvent.ALT_MASK that must be
051: // present in the mouse-pressed event for a drag to start.
052:
053: private Controller onUserAction; // notified each time the mouse moves during a drag
054: private Controller onFinishDrag; // notified when the user finishes a drag
055:
056: /**
057: * Create a Panner object that will let the user pan the CoordinateRect
058: * that contains the Panner by
059: * right-clicking and dragging (or, on Macintosh, command-clicking).
060: */
061: public Panner() {
062: this (MouseEvent.META_MASK);
063: }
064:
065: /**
066: * Create a Panner object that will let the user click-and-drag to pan the CoordinateRect
067: * that contains the Panner. The mouse-pressed event must have the specfied set of
068: * modifiers set.
069: *
070: * @param modifiers If the value is zero, the user drags the CoordinateRect by clicking without
071: * pressing any modifier keys. Otherwise, the value should be a combination of
072: * one or more of the constants MouseEvent.SHIFT_MASK, MouseEvent.CTRL_MASK,
073: * MouseEvent.META_MASK, and MouseEvent.ALT_MASK, or'ed together. (Remember
074: * that right-clicking sets META_MASK and clicking with a middle mouse button
075: * sets ALT_MASK.)
076: *
077: */
078: public Panner(int modifierSet) {
079: modifiers = modifierSet
080: & (MouseEvent.SHIFT_MASK | MouseEvent.CTRL_MASK
081: | MouseEvent.META_MASK | MouseEvent.ALT_MASK);
082: }
083:
084: /**
085: * Set a Controller that will be notified (by calling its compute method) whenever
086: * the user moves the mouse during a drag. If the value is null, no Controller is
087: * notified. Note that Drawables generally redraw themselvs correctly during the
088: * drag anyway, without any Controller being involved. Even if there are other
089: * things that need to be computed, it's probably better to compute them only once
090: * at the end of the drag. Do this by calling setOnFinishDrag() instead of this method.
091: */
092: public void setOnUserAction(Controller c) {
093: onUserAction = c;
094: }
095:
096: /**
097: * Get the Controller that is notified when the user moves the mouse during a drag.
098: * Returns null if no Controller is notified.
099: */
100: public Controller getOnUserAction() {
101: return onUserAction;
102: }
103:
104: /**
105: * Set a Controller that will be notified (by calling its compute method) whenever
106: * the user finishes a drag operation. If the value is null, no Controller is notified.
107: * You only need to do this if you have to recompute some object that depends on the
108: * coordinate limits of the CoordinateRect that contains this Panner object.
109: * Presumably, this will be an object that depends on one if the Value objects returned
110: * by the getValueObject() method in the CoordinatRect class.
111: */
112: public void setOnFinishDrag(Controller c) {
113: onFinishDrag = c;
114: }
115:
116: /**
117: * Get the Controller that is notified when the user finishs a drag.
118: * Returns null if no Controller is notified.
119: */
120: public Controller getOnFinishDrag() {
121: return onFinishDrag;
122: }
123:
124: /**
125: * Called when this object is added to a DisplayCanvas. Not meant to be called directly.
126: */
127: protected void setOwnerData(DisplayCanvas canvas,
128: CoordinateRect coords) {
129: // Called automatically when this object is added to canvas.
130: if (canvas != null) {
131: canvas.removeMouseListener(this );
132: canvas.removeMouseMotionListener(this );
133: }
134: super .setOwnerData(canvas, coords);
135: if (canvas != null) {
136: canvas.addMouseListener(this );
137: canvas.addMouseMotionListener(this );
138: }
139: }
140:
141: /**
142: * Override the abstract draw() method from the Drawable class. This
143: * is defined to be empty since a Panner object has no visible representation.
144: */
145: public void draw(Graphics g, boolean coordsChanged) {
146: }
147:
148: private boolean dragging;
149: private int prevX, prevY;
150:
151: /**
152: * Responds to a mouse-press. Not meant to be called directly.
153: */
154: public void mousePressed(MouseEvent evt) {
155: dragging = false;
156: if (evt.isConsumed())
157: return;
158: if (!getVisible() || canvas == null || coords == null)
159: return;
160: if ((evt.getModifiers() & modifiers) != modifiers)
161: return;
162: prevX = evt.getX();
163: prevY = evt.getY();
164: if (prevX < coords.getLeft()
165: || prevX >= coords.getLeft() + coords.getWidth()
166: || prevY < coords.getTop()
167: || prevY >= coords.getTop() + coords.getHeight())
168: return;
169: evt.consume();
170: dragging = true;
171: }
172:
173: /**
174: * Responds to a mouse-drag. Not meant to be called directly.
175: */
176: public void mouseDragged(MouseEvent evt) {
177: if (!dragging)
178: return;
179: evt.consume();
180: if (evt.getX() == prevX && evt.getY() == prevY)
181: return;
182: double[] limits = coords.getLimits();
183: if (limits == null)
184: return;
185: double xOffset = (evt.getX() - prevX) * coords.getPixelWidth();
186: double yOffset = (evt.getY() - prevY) * coords.getPixelHeight();
187: coords.setLimits(limits[0] - xOffset, limits[1] - xOffset,
188: limits[2] + yOffset, limits[3] + yOffset);
189: needsRedraw();
190: if (onUserAction != null)
191: onUserAction.compute();
192: prevX = evt.getX();
193: prevY = evt.getY();
194: }
195:
196: /**
197: * Responds to a mouse-release. Not meant to be called directly.
198: */
199: public void mouseReleased(MouseEvent evt) {
200: if (!dragging)
201: return;
202: evt.consume();
203: mouseDragged(evt);
204: dragging = false;
205: if (onFinishDrag != null)
206: onFinishDrag.compute();
207: }
208:
209: /**
210: * Responds to a mouse-click. Not meant to be called directly.
211: * Defined to be empty in this class.
212: */
213: public void mouseClicked(MouseEvent evt) {
214: }
215:
216: /**
217: * Responds when mouse moves. Not meant to be called directly.
218: * Defined to be empty in this class.
219: */
220: public void mouseMoved(MouseEvent evt) {
221: }
222:
223: /**
224: * Responds to a mouse-enter event. Not meant to be called directly.
225: * Defined to be empty in this class.
226: */
227: public void mouseEntered(MouseEvent evt) {
228: }
229:
230: /**
231: * Responds to a mouse-exit event. Not meant to be called directly.
232: * Defined to be empty in this class.
233: */
234: public void mouseExited(MouseEvent evt) {
235: }
236:
237: } // end class Panner
|