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 edu.hws.jcm.awt.*;
026: import edu.hws.jcm.data.Value;
027: import java.awt.*;
028: import java.util.Vector;
029:
030: /**
031: * A CoordinateRect represents a rectagular region in the xy-plane, specified
032: * by values xmin,xmax,ymin,ymax. The conditions ymin < ymax and xmin < xmax
033: * are enforced. (Values are swapped if necessary, and if min==max, they are
034: * reset to -1 and +1. If any of the values are set to an infinite or NaN
035: * value, then the coordinate rect won't display anything except the message
036: * "Error: undefined limits".)
037: * <P> When the Rect is mapped onto the screen, there can be a gap of a specified
038: * number of pixels between the min,max values and the edges of the rectangle
039: * on the screen. If the gap is non-zero, then the actual range of coordinates
040: * on the rect is larger than the range from the specifed min to max. (This is
041: * done mainly so I could have axes that don't quite reach the edges of the rect.)
042: * <P>A CoordinateRect maintains a list of Drawable items. When the Rect's
043: * draw() method is called, it calls the draw() method of each of the Drawable
044: * items it contains. When its compute() method is called, it calls the
045: * compute() method of any Drawable that is a Computable. When its checkInput()
046: * method is called, it calls the checkInput() method of any Drawable that is
047: * an InputObject.
048: * <P>A CoordinateRect represents a rectangular region in a DisplayCanvas.
049: * It has a reference to that Canvas, which is set automatically when it is
050: * added to the canvas. If the size, range, or gap on the CoordinateRect
051: * change, it will ask the Canvas to redraw the area it occupies.
052: *
053: * <P>The values of xmin, xmax, ymin, ymax are exported as Value objects,
054: * which can be used elsewhere in your program. The Value objects can
055: * be obtained by calling getValueObject(). If you do this, you should
056: * add the objects that depend on those values to a Controller and
057: * register the Controller to listen for changes from this CoordinateRect
058: * by calling the CoordinateRect.setOnChange(Controller) method.
059: */
060:
061: public class CoordinateRect implements Tieable, Limits, Computable,
062: InputObject {
063:
064: private double xmin, xmax, ymin, ymax; // Range of x and y values on the Rect (not counting the gap).
065:
066: private int gap = 5; //Extra pixels around the edges, outside the specifed range of x,y values.
067: //Note: xmin,xmax,ymin,ymax are the limits on a rectangle that
068: //is inset from the drawing rect by gap pixels on each edge.
069:
070: /**
071: * Drawable items contained in this CoordinateRect
072: */
073: protected Vector drawItems = new Vector();
074:
075: /**
076: * Set to true when one of the limits or the gap has changed.
077: */
078: protected boolean changed;
079:
080: private long serialNumber; // This value is increased whenever xmin,xmax,ymin,ymax,gap change
081: // or when the size of the rectangle in pixels changes.
082:
083: /**
084: * This contains other Limit objects with which the CoordinateRect is
085: * synchronizing. This is ordinarily managed by a LimitControlPanel,
086: * so you don't have to worry about it. (However, you can also sync
087: * several CoordinateRects even in the absense of a LimitControlPanel.
088: * To do so, create the Tie that ties the CoordinateRect and pass it to
089: * the setSyncWith() method of each CoordinateRect. It is NOT necessary
090: * to add the Tie to a Controller. Synchronization is handled by the
091: * CoordinateRects themselves.
092: */
093: protected Tie syncWith;
094:
095: /**
096: * Create a CoordinateRect with default limits: -5, 5, -5, 5.
097: */
098: public CoordinateRect() {
099: this (-5, 5, -5, 5);
100: }
101:
102: /**
103: * Create a CoordinateRect with specified limits.
104: */
105: public CoordinateRect(double xmin, double xmax, double ymin,
106: double ymax) {
107: setLimits(xmin, xmax, ymin, ymax);
108: serialNumber = 0;
109: setRestoreBuffer(); // Restore buffer holds original limits, util it is reset
110: }
111:
112: //--------- Methods for getting and setting xmin, xmax, ymin, ymax, and gap.------
113:
114: /**
115: * Get the mimimum x-coordinate.
116: */
117: public double getXmin() {
118: return xmin;
119: }
120:
121: /**
122: * Get the maximum x-coordinate.
123: */
124: public double getXmax() {
125: return xmax;
126: }
127:
128: /**
129: * Get the mimimum y-coordinate.
130: */
131: public double getYmin() {
132: return ymin;
133: }
134:
135: /**
136: * Get the maximum x-coordinate.
137: */
138: public double getYmax() {
139: return ymax;
140: }
141:
142: /**
143: * Get the gap, in pixels, between the edges of
144: * the CoordinateRect and the limits specified by xmin, xmax, ymin, and ymax.
145: */
146: public int getGap() {
147: return gap;
148: }
149:
150: /**
151: * Set the gap. This is ignored if g is less than zero. This gap is the number of pixels
152: * between the edges of the CoordinateRect and the limits specified by xmin, xmax, ymin, and ymax.
153: * The default value is 5.
154: *
155: */
156: public void setGap(int g) {
157: if (g >= 0 && gap != g) {
158: int oldgap = gap;
159: gap = g;
160: changed = true;
161: serialNumber++;
162: needsRedraw();
163: }
164: }
165:
166: /**
167: * Get an array containing the limits on the CoordinateRect in the order xmin, xmax, ymin, ymax.
168: */
169: public double[] getLimits() {
170: return new double[] { xmin, xmax, ymin, ymax };
171: }
172:
173: /**
174: * Set the limits on the CoordinteRect
175: *
176: * @param xmin the minimum x-coordinate on the CoordinateRect
177: * @param xmax the maximum x-coordinate on the CoordinateRect
178: * @param ymin the minimum y-coordinate on the CoordinateRect
179: * @param ymax the maximum y-coordinate on the CoordinateRect
180: */
181: public void setLimits(double xmin, double xmax, double ymin,
182: double ymax) {
183: double[] oldLimits = getLimits();
184: this .xmin = xmin;
185: this .xmax = xmax;
186: this .ymin = ymin;
187: this .ymax = ymax;
188: checkLimits();
189: double[] newLimits = getLimits();
190: if (oldLimits[0] == newLimits[0]
191: && oldLimits[1] == newLimits[1]
192: && oldLimits[2] == newLimits[2]
193: && oldLimits[3] == newLimits[3])
194: return;
195: changed = true;
196: serialNumber++;
197: if (syncWith != null)
198: syncWith.check();
199: if (onChange != null)
200: onChange.compute();
201: needsRedraw();
202: }
203:
204: /**
205: * Set the coordinate limits from array; extra elements in array are ignored.
206: * This is ignored if the array is null or has fewer than 4 members.
207: * The order of values in the array is xmin, xmax, ymin, ymax.
208: *
209: */
210: public void setLimits(double[] d) {
211: if (d != null && d.length >= 4)
212: setLimits(d[0], d[1], d[2], d[3]);
213: }
214:
215: /**
216: * Specify a controller to be notified when the limits on this
217: * CoordinateRect change.
218: */
219: public void setOnChange(Controller c) {
220: onChange = c;
221: }
222:
223: /**
224: * Get the controller that is notified when the limits on this
225: * CoordinateRect change. This can be null.
226: */
227: public Controller getOnChange() {
228: return onChange;
229: }
230:
231: /**
232: * Get a Value object representing one of the limits on this CoordinateRect.
233: * The parameter should be one of the constants CoordinateRect.XMIN,
234: * CoordinateRect.XMAX, CoordinateRect.YMIN, or CoordinateRect.YMAX.
235: * (If not, it is treated the same as YMAX).
236: *
237: */
238: public Value getValueObject(final int which) {
239: return new Value() {
240: public double getVal() {
241: switch (which) {
242: case XMIN:
243: return getXmin();
244: case XMAX:
245: return getXmax();
246: case YMIN:
247: return getYmin();
248: default:
249: return getYmax();
250: }
251: }
252: };
253: }
254:
255: /**
256: * Return the serial number of the CoordinateRect, which is incremented each time the limits change.
257: * Part of the Tieable interface.
258: * Not meant to be called directly.
259: */
260: public long getSerialNumber() {
261: return serialNumber;
262: }
263:
264: /**
265: * Set the Tie object that is used to synchronize this CoordinareRect with other objects.
266: * This is ordinarily called by a LimitControlPanel, so you don't have to worry about it.
267: */
268: public void setSyncWith(Tie tie) {
269: syncWith = tie;
270: }
271:
272: /**
273: * Part of the Tieable interface.
274: * Not meant to be called directly.
275: */
276: public void sync(Tie tie, Tieable newest) {
277: if (newest != this ) {
278: if (!(newest instanceof Limits))
279: throw new IllegalArgumentException(
280: "Internal programming error: A CoordinateRect can only be tied to a Limits object.");
281: double[] d = ((Limits) newest).getLimits();
282: if (d != null && d.length >= 4) {
283: double[] oldLimits = getLimits();
284: if (d[0] == oldLimits[0] && d[1] == oldLimits[1]
285: && d[2] == oldLimits[2] && d[3] == oldLimits[3])
286: return;
287: xmin = d[0];
288: xmax = d[1];
289: ymin = d[2];
290: ymax = d[3];
291: checkLimits();
292: serialNumber = newest.getSerialNumber();
293: changed = true;
294: if (onChange != null)
295: onChange.compute();
296: needsRedraw();
297: }
298: }
299: }
300:
301: private void checkLimits() { //Make sure limits satisfy constraints.
302: if (xmin == xmax) {
303: xmin -= 1;
304: xmax += 1;
305: } else if (xmin > xmax) {
306: double temp = xmin;
307: xmin = xmax;
308: xmax = temp;
309: }
310: if (ymin == ymax) {
311: ymin -= 1;
312: ymax += 1;
313: }
314: if (ymin > ymax) {
315: double temp = ymin;
316: ymin = ymax;
317: ymax = temp;
318: }
319: }
320:
321: // -------------- Value objects corresponding to xmin, xmax, ymin, ymax -------------
322:
323: /**
324: * A constant for use with the getValueObject() method to specify which Value is to be returned.
325: * XMIN specifies that the Value is the minimum x-coordinate on the CoordinateRect.
326: */
327: public final static int XMIN = 0;
328:
329: /**
330: * A constant for use with the getValueObject() method to specify which Value is to be returned.
331: * XMAX specifies that the Value is the maximum x-coordinate on the CoordinateRect.
332: */
333: public final static int XMAX = 1;
334:
335: /**
336: * A constant for use with the getValueObject() method to specify which Value is to be returned.
337: * YMIN specifies that the Value is the minimum y-coordinate on the CoordinateRect.
338: */
339: public final static int YMIN = 2;
340:
341: /**
342: * A constant for use with the getValueObject() method to specify which Value is to be returned.
343: * YMAX specifies that the Value is the maximum y-coordinate on the CoordinateRect.
344: */
345: public final static int YMAX = 3;
346:
347: /**
348: * If non-null, this is the Controller that is notified when the limits change.
349: */
350: protected Controller onChange;
351:
352: // ---------------------- Methods for working with Pixels ----------------------
353: // Note: This stuff is only valid if the CoordinateRect is
354: // displayed in a Graphics context. I.E., after a call to draw();
355: // It is meant to be used by Drawables when their draw() methods are called.
356:
357: private int left, top, width = -1, height = -1; // Not setable; these are valid only during drawing and are meant to be used
358:
359: // by the Drawables in this Coorfdinate Rect.
360:
361: /**
362: * Get the left edge of this CoordinateRect in the DisplayCanvas that contains it.
363: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
364: * mainly to be used by Drawables in this CoordinateRect.)
365: */
366: public int getLeft() {
367: return left;
368: }
369:
370: /**
371: * Get the width in pixels of this CoordinateRect in the DisplayCanvas that contains it.
372: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
373: * mainly to be used by Drawables in this CoordinateRect.)
374: */
375: public int getWidth() {
376: return width;
377: }
378:
379: /**
380: * Get the top edge of this CoordinateRect in the DisplayCanvas that contains it.
381: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
382: * mainly to be used by Drawables in this CoordinateRect.)
383: */
384: public int getTop() {
385: return top;
386: }
387:
388: /**
389: * Get the height in pixels of this CoordinateRect in the DisplayCanvas that contains it.
390: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
391: * mainly to be used by Drawables in this CoordinateRect.)
392: */
393: public int getHeight() {
394: return height;
395: }
396:
397: /**
398: * Return the width of one pixel in this coordinate system.
399: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
400: * mainly to be used by Drawables in this CoordinateRect.)
401: *
402: */
403: public double getPixelWidth() {
404: return (xmax - xmin) / (width - 2 * gap - 1);
405: }
406:
407: /**
408: * Return the height of one pixel in this coordinate system.
409: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
410: * mainly to be used by Drawables in this CoordinateRect.)
411: *
412: */
413: public double getPixelHeight() {
414: return (ymax - ymin) / (height - 2 * gap - 1);
415: }
416:
417: /**
418: * Convert an x-coodinate into a horizontal pixel coordinate.
419: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
420: * mainly to be used by Drawables in this CoordinateRect.)
421: *
422: */
423: public int xToPixel(double x) {
424: int xInt = left
425: + gap
426: + (int) ((x - xmin) / (xmax - xmin) * (width - 2 * gap - 1));
427: if (xInt < -32000)
428: return -32000;
429: else if (xInt > 32000)
430: return 32000;
431: else
432: return xInt;
433: }
434:
435: /**
436: * Convert a y-coodinate into a vertical pixel coordinate.
437: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
438: * mainly to be used by Drawables in this CoordinateRect.)
439: *
440: */
441: public int yToPixel(double y) {
442: int yInt = top
443: + gap
444: + (int) ((ymax - y) / (ymax - ymin) * (height - 2 * gap - 1));
445: if (yInt < -32000)
446: return -32000;
447: else if (yInt > 32000)
448: return 32000;
449: else
450: return yInt;
451: }
452:
453: /**
454: * Convert a horizontal pixel coordinate into an x-coordinate.
455: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
456: * mainly to be used by Drawables in this CoordinateRect.)
457: *
458: */
459: public double pixelToX(int h) {
460: return xmin + ((h - left - gap) * (xmax - xmin))
461: / (width - 2 * gap - 1);
462: }
463:
464: /**
465: * Convert a vertical pixel coordinate into a y-coordinate.
466: * (This is only valid when the CoordinateRect has actually been displayed. It is meant
467: * mainly to be used by Drawables in this CoordinateRect.)
468: *
469: */
470: public double pixelToY(int y) {
471: return ymax - ((y - top - gap) * (ymax - ymin))
472: / (height - 2 * gap - 1);
473: }
474:
475: // ---------------------- Save/Restore limits -------------------------
476:
477: private double restore_xmin = Double.NaN, restore_xmax,
478: restore_ymin, restore_ymax;
479:
480: /**
481: * A CoordinateRect can store its current limits in a buffer. These limits
482: * can be restored by a call to this method. Only one level of
483: * save/restore is provided. If limits have not been saved, then nothing happens.
484: * The original limits on the CoordinateRect are saves automatically when
485: * the CoordinateRect is first created.
486: *
487: * @return an array containing new limits.
488: */
489: public double[] restore() {
490: if (Double.isNaN(restore_xmin))
491: return null;
492: setLimits(restore_xmin, restore_xmax, restore_ymin,
493: restore_ymax);
494: return getLimits();
495: }
496:
497: /**
498: * A CoordinateRect can store its current limits in a buffer. This method
499: * clears that buffer.
500: */
501: public void clearRestoreBuffer() {
502: restore_xmin = Double.NaN;
503: }
504:
505: /**
506: * Save current limits in buffer. They can be restored later by a call
507: * to the restore() method. Only one level of
508: * save/restore is provided.
509: */
510: public void setRestoreBuffer() {
511: if (badData())
512: return;
513: checkLimits();
514: restore_xmin = xmin;
515: restore_xmax = xmax;
516: restore_ymin = ymin;
517: restore_ymax = ymax;
518: }
519:
520: /**
521: * Used to test if any of the limit data are infinite or NaN.
522: */
523: private boolean badData() {
524: return Double.isNaN(xmin) || Double.isInfinite(xmin)
525: || Double.isNaN(ymin) || Double.isInfinite(ymin)
526: || Double.isNaN(xmax) || Double.isInfinite(xmax)
527: || Double.isNaN(ymax) || Double.isInfinite(ymax);
528: }
529:
530: // ----------- Zoom in and out ---------------
531:
532: /**
533: * Change limits to zoom in by a factor of 2. A maximal zoom is enforced.
534: * The center of the rectangle does not move.
535: *
536: * @return an array of the new limits, or null if limits don't change.
537: */
538: public double[] zoomIn() {
539: if (badData())
540: return getLimits();
541: double halfwidth = (xmax - xmin) / 4.0;
542: double halfheight = (ymax - ymin) / 4.0;
543: double centerx = (xmin + xmax) / 2.0;
544: double centery = (ymin + ymax) / 2.0;
545: if (Math.abs(halfheight) < 1e-100
546: || Math.abs(halfwidth) < 1e-100)
547: return null;
548: setLimits(centerx - halfwidth, centerx + halfwidth, centery
549: - halfheight, centery + halfheight);
550: return getLimits();
551: }
552:
553: /**
554: * Change limits to zoom out by a factor of 2. A maximal zoom is enforced.
555: * The center of the rectangle does not move.
556: *
557: * @return an array of the new limits, or null if limits don't change.
558: */
559: public double[] zoomOut() {
560: if (badData())
561: return getLimits();
562: double halfwidth = (xmax - xmin);
563: double halfheight = (ymax - ymin);
564: double centerx = (xmin + xmax) / 2.0;
565: double centery = (ymin + ymax) / 2.0;
566: if (Math.abs(halfwidth) > 1e100 || Math.abs(halfheight) > 1e100)
567: return null;
568: setLimits(centerx - halfwidth, centerx + halfwidth, centery
569: - halfheight, centery + halfheight);
570: return getLimits();
571: }
572:
573: /**
574: * Change limits to zoom in by a factor of 2, centered on a specified point. A maximal zoom is enforced.
575: * The point does not move. Only valid when CoordinateRect is
576: * displayed in a rectangle on the screen.
577: *
578: * @param x the horizontal pixel coordinate of the center point of the zoom
579: * @param y the vertical pixel coordinate of the center point of the zoom
580: *
581: * @return an array of the new limits, or null if limits don't change.
582: */
583: public double[] zoomInOnPixel(int x, int y) {
584: if (badData())
585: return getLimits();
586: double halfwidth = (xmax - xmin) / 4.0;
587: double halfheight = (ymax - ymin) / 4.0;
588: if (Math.abs(halfheight) < 1e-100
589: || Math.abs(halfwidth) < 1e-100)
590: return null;
591: double xclick = pixelToX(x);
592: double yclick = pixelToY(y);
593: double centerx = (xmin + xmax) / 2;
594: double centery = (ymin + ymax) / 2;
595: double newCenterx = (centerx + xclick) / 2;
596: double newCentery = (centery + yclick) / 2;
597: setLimits(newCenterx - halfwidth, newCenterx + halfwidth,
598: newCentery - halfheight, newCentery + halfheight);
599: return getLimits();
600: }
601:
602: /**
603: * Change limits to zoom out by a factor of 2, centered on a specified point. A maximal zoom is enforced.
604: * The point (x,y) does not move. Valid only if CoordinateRect has been drawn.
605: *
606: * @param x the horizontal pixel coordinate of the center point of the zoom
607: * @param y the vertical pixel coordinate of the center point of the zoom
608: *
609: * @return an array of the new limits, or null if limits don't change.
610: */
611: public double[] zoomOutFromPixel(int x, int y) {
612: if (badData())
613: return getLimits();
614: double halfwidth = (xmax - xmin);
615: double halfheight = (ymax - ymin);
616: if (Math.abs(halfwidth) > 1e100 || Math.abs(halfheight) > 1e100)
617: return null;
618: double xclick = pixelToX(x);
619: double yclick = pixelToY(y);
620: double centerx = (xmin + xmax) / 2;
621: double centery = (ymin + ymax) / 2;
622: double newCenterx = 2 * centerx - xclick;
623: double newCentery = 2 * centery - yclick;
624: setLimits(newCenterx - halfwidth, newCenterx + halfwidth,
625: newCentery - halfheight, newCentery + halfheight);
626: return getLimits();
627: }
628:
629: /**
630: * Reset limits, if necessary, so scales on the axes are the same.
631: * Only valid of the CoordinateRect has been drawn.
632: *
633: * @return an array with the new limits, or null if limits don't change.
634: */
635: public double[] equalizeAxes() {
636: if (badData())
637: return getLimits();
638: double w = xmax - xmin;
639: double h = ymax - ymin;
640: double pixelWidth = w / (width - 2 * gap - 1);
641: double pixelHeight = h / (height - 2 * gap - 1);
642: double newXmin, newXmax, newYmin, newYmax;
643: if (pixelWidth < pixelHeight) {
644: double centerx = (xmax + xmin) / 2;
645: double halfwidth = w / 2 * pixelHeight / pixelWidth;
646: newXmax = centerx + halfwidth;
647: newXmin = centerx - halfwidth;
648: newYmin = ymin;
649: newYmax = ymax;
650: } else if (pixelWidth > pixelHeight) {
651: double centery = (ymax + ymin) / 2;
652: double halfheight = h / 2 * pixelWidth / pixelHeight;
653: newYmax = centery + halfheight;
654: newYmin = centery - halfheight;
655: newXmin = xmin;
656: newXmax = xmax;
657: } else
658: return null;
659: setLimits(newXmin, newXmax, newYmin, newYmax);
660: return getLimits();
661: }
662:
663: // ------------------------------ Drawing ----------------------------
664:
665: private DisplayCanvas canvas; // The canvas in which this CoordinateRect is displayed. This is set
666:
667: // automatically when the CoordinateRect is added to or removed from
668: // a DisplayCanvas, and it should not be changed.
669:
670: /**
671: * This is meant to be called only by the DisplayCanvas class,
672: * when this CoordinateRect is added to ta DisplayCanvas.
673: *
674: */
675: void setOwner(DisplayCanvas canvas) {
676: this .canvas = canvas;
677: }
678:
679: private void needsRedraw() { //Notifies the canvas that the area occupied by this CoodinateRect
680: if (canvas != null) //needs to be redrawn.
681: canvas.doRedraw(this );
682: }
683:
684: /**
685: * When this is called, the CoordinateRect will call the
686: * checkInput method of any Drawable it contains that is
687: * also an InputObject. This is ordinarly only called by a DisplayCanvas.
688: */
689: public void checkInput() {
690: int ct = drawItems.size();
691: for (int i = 0; i < ct; i++)
692: if (drawItems.elementAt(i) instanceof InputObject)
693: ((InputObject) drawItems.elementAt(i)).checkInput();
694: }
695:
696: /**
697: * When this is called, the CoordinateRect will call the compute method
698: * of any Drawable it contains that is also a Computable.
699: * This is ordinarly only called by a DisplayCanvas.
700: */
701: public void compute() {
702: int ct = drawItems.size();
703: for (int i = 0; i < ct; i++)
704: if (drawItems.elementAt(i) instanceof Computable)
705: ((Computable) drawItems.elementAt(i)).compute();
706: }
707:
708: /**
709: * Method required by InputObject interface; in this class, it calls the same method
710: * recursively on any input objects containted in this CoordinateRect. This is meant to
711: * be called by JCMPanel.gatherInputs().
712: */
713: public void notifyControllerOnChange(Controller c) {
714: int ct = drawItems.size();
715: for (int i = 0; i < ct; i++)
716: if (drawItems.elementAt(i) instanceof InputObject)
717: ((InputObject) drawItems.elementAt(i))
718: .notifyControllerOnChange(c);
719: }
720:
721: /**
722: * Add a drawable item to the CoordinateRect.
723: *
724: */
725: synchronized public void add(Drawable d) {
726: if (d != null && !drawItems.contains(d)) {
727: d.setOwnerData(canvas, this );
728: drawItems.addElement(d);
729: }
730: }
731:
732: /**
733: * Remove the given Drawable item, if present in this CoordinateRect.
734: *
735: */
736: synchronized public void remove(Drawable d) {
737: if (d != null && drawItems.removeElement(d))
738: d.setOwnerData(null, null);
739: }
740:
741: /**
742: * Returns the number of Drawable items that are in this CoordinateRect.
743: */
744: public int getDrawableCount() {
745: return (drawItems == null) ? 0 : drawItems.size();
746: }
747:
748: /**
749: * Get the i-th Drawable in this Rect, or null if i is less than zero
750: * or greater than or equal to the number of items.
751: *
752: * @param i The number of the item to be returned, where the first item is number zero.
753: */
754: public Drawable getDrawable(int i) {
755: if (drawItems != null && i >= 0 && i < drawItems.size())
756: return (Drawable) drawItems.elementAt(i);
757: else
758: return null;
759: }
760:
761: /**
762: * Check whether a mouse click (as specified in the MouseEvent parameter) is
763: * a click on a Draggable item that wants to be dragged. If so, return that item. If not, return null.
764: * This is meant to be called only by DisplayCanvas.
765: */
766: Draggable checkDraggables(java.awt.event.MouseEvent evt) {
767: int top = drawItems.size();
768: for (int i = top - 1; i >= 0; i--)
769: if (drawItems.elementAt(i) instanceof Draggable) {
770: if (((Draggable) drawItems.elementAt(i)).startDrag(evt))
771: return (Draggable) drawItems.elementAt(i);
772: }
773: return null;
774: }
775:
776: /**
777: * Draw in rect with upperleft corner (0,0) and specified width,height.
778: * This is not ordinarily called directly.
779: *
780: */
781: public void draw(Graphics g, int width, int height) {
782: draw(g, 0, 0, width, height);
783: }
784:
785: /**
786: * Draw in specified rect. This is not ordinarily called directly.
787: */
788: synchronized public void draw(Graphics g, int left, int top,
789: int width, int height) {
790: if (badData()) {
791: g.setColor(Color.red);
792: g.drawRect(left, top, width - 1, height - 1);
793: g.drawString("(undefined limits)", left + 6, top + 15);
794: }
795: if (changed || this .left != left || this .top != top
796: || this .width != width || this .height != height) {
797: this .width = width;
798: this .height = height;
799: this .left = left;
800: this .top = top;
801: checkLimits();
802: changed = true;
803: }
804: doDraw(g);
805: changed = false;
806: }
807:
808: /**
809: * Draw all the Drawable items. This is called by the draw() method and is not
810: * meant to be called directly. However, it might be overridden in a subclass.
811: *
812: */
813: protected void doDraw(Graphics g) {
814: int ct = drawItems.size();
815: for (int i = 0; i < ct; i++) {
816: Drawable d = (Drawable) drawItems.elementAt(i);
817: if (d.getVisible())
818: d.draw(g, changed);
819: }
820: }
821:
822: } // end class CoordinateRect
|