Source Code Cross Referenced for MouseReshapeTracker.java in  » GIS » GeoTools-2.4.1 » org » geotools » gui » swing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.gui.swing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
0005:         *    (C) 2001, Institut de Recherche pour le Développement
0006:         *
0007:         *    This library is free software; you can redistribute it and/or
0008:         *    modify it under the terms of the GNU Lesser General Public
0009:         *    License as published by the Free Software Foundation; either
0010:         *    version 2.1 of the License, or (at your option) any later version.
0011:         *
0012:         *    This library is distributed in the hope that it will be useful,
0013:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015:         *    Lesser General Public License for more details.
0016:         */
0017:        package org.geotools.gui.swing;
0018:
0019:        // Geometry
0020:        import java.awt.Shape;
0021:        import java.awt.Rectangle;
0022:        import java.awt.geom.Point2D;
0023:        import java.awt.geom.Rectangle2D;
0024:        import java.awt.geom.PathIterator;
0025:        import java.awt.geom.AffineTransform;
0026:        import java.awt.geom.RectangularShape;
0027:        import java.awt.geom.NoninvertibleTransformException;
0028:        import org.geotools.referencing.operation.matrix.XAffineTransform;
0029:
0030:        // Graphics
0031:        import java.awt.Paint;
0032:        import java.awt.Color;
0033:        import java.awt.Graphics;
0034:        import java.awt.Graphics2D;
0035:
0036:        // User interface
0037:        import java.awt.Cursor;
0038:        import java.awt.Insets;
0039:        import java.awt.Component;
0040:        import javax.swing.JFrame;
0041:        import javax.swing.JSpinner;
0042:        import javax.swing.KeyStroke;
0043:        import javax.swing.JComponent;
0044:        import javax.swing.JTextField;
0045:        import javax.swing.SwingConstants;
0046:        import javax.swing.SpinnerDateModel;
0047:        import javax.swing.SpinnerNumberModel;
0048:        import javax.swing.text.JTextComponent;
0049:        import javax.swing.JFormattedTextField;
0050:
0051:        // Events
0052:        import java.awt.event.KeyEvent;
0053:        import java.awt.event.MouseEvent;
0054:        import java.awt.event.ActionEvent;
0055:        import java.beans.PropertyChangeEvent;
0056:        import java.beans.PropertyChangeListener;
0057:        import javax.swing.event.MouseInputAdapter;
0058:
0059:        // Formats
0060:        import java.util.Date;
0061:        import java.text.Format;
0062:        import java.text.DateFormat;
0063:        import java.text.DecimalFormat;
0064:        import java.text.ParseException;
0065:        import java.text.SimpleDateFormat;
0066:
0067:        // Miscellaneous
0068:        import java.lang.Double;
0069:        import java.io.IOException;
0070:        import java.io.ObjectInputStream;
0071:
0072:        // Resources
0073:        import org.geotools.resources.XMath;
0074:        import org.geotools.resources.XArray;
0075:        import org.geotools.resources.Utilities;
0076:
0077:        /**
0078:         * Controls the position and size of a rectangle which the user can move
0079:         * with their mouse. For example, this class can be used as follows:
0080:         *
0081:         * <blockquote><pre>
0082:         * public class MyClass extends JPanel
0083:         * {
0084:         *     private final MouseReshapeTracker <em>slider</em> = new MouseReshapeTracker()
0085:         *     {
0086:         *         protected void {@link #clipChangeRequested clipChangeRequested}(double xmin, double xmax, double ymin, double ymax) {
0087:         *             // Indicates what must be done if the user tries to move the
0088:         *             // rectangle outside the permitted limits.
0089:         *             // This method is optional.
0090:         *         }
0091:         *
0092:         *         protected void {@link #stateChanged stateChanged}(boolean isAdjusting) {
0093:         *             // Method automatically called each time the user
0094:         *             // changes the position of the rectangle.
0095:         *             // Code here what it should do in this case.
0096:         *         }
0097:         *     };
0098:         *
0099:         *     private final AffineTransform transform = AffineTransform.getScaleInstance(10, 10);
0100:         *
0101:         *     public MyClass() {
0102:         *         <em>slider</em>.{@link #setFrame     setFrame}(0, 0, 1, 1);
0103:         *         <em>slider</em>.{@link #setClip      setClip}(0, 100, 0, 1);
0104:         *         <em>slider</em>.{@link #setTransform setTransform}(transform);
0105:         *         addMouseMotionListener(<em>slider</em>);
0106:         *         addMouseListener      (<em>slider</em>);
0107:         *     }
0108:         *
0109:         *     public void paintComponent(Graphics graphics) {
0110:         *         AffineTransform tr=...
0111:         *         Graphics2D g = (Graphics2D) graphics;
0112:         *         g.transform(transform);
0113:         *         g.setColor(new Color(128, 64, 92, 64));
0114:         *         g.fill    (<em>slider</em>);
0115:         *     }
0116:         * }
0117:         * </pre></blockquote>
0118:         *
0119:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/MouseReshapeTracker.java $
0120:         * @version $Id: MouseReshapeTracker.java 22710 2006-11-12 18:04:54Z desruisseaux $
0121:         * @author Martin Desruisseaux
0122:         */
0123:        class MouseReshapeTracker extends MouseInputAdapter implements  Shape {
0124:            /**
0125:             * Minimum width the rectangle should have, in pixels.
0126:             */
0127:            private static final int MIN_WIDTH = 12;
0128:
0129:            /**
0130:             * Minimum height the rectangle should have, in pixels.
0131:             */
0132:            private static final int MIN_HEIGHT = 12;
0133:
0134:            /**
0135:             * If the user moves the mouse by less than RESIZE_POS, then we assume the
0136:             * user wants to resize rather than move the rectangle. This distance is
0137:             * measured in pixels from one of the rectangle's edges.
0138:             */
0139:            private static final int RESIZE_POS = 4;
0140:
0141:            /**
0142:             * Minimum value of the <code>(clipped rectangle size)/(full rectangle
0143:             * size)</code> ratio. This minimum value will only be taken into
0144:             * account when the user modifies the rectangle's position using the values
0145:             * entered in the fields. This number must be greater than or equal to 1.
0146:             */
0147:            private static final double MINSIZE_RATIO = 1.25;
0148:
0149:            /**
0150:             * Minimum <var>x</var> coordinate permitted for the rectangle. The default
0151:             * value is {@link java.lang.Double#NEGATIVE_INFINITY}.
0152:             */
0153:            private double xmin = Double.NEGATIVE_INFINITY;
0154:
0155:            /**
0156:             * Minimum <var>y</var> coordinate permitted for the rectangle. The default
0157:             * value is {@link java.lang.Double#NEGATIVE_INFINITY}.
0158:             */
0159:            private double ymin = Double.NEGATIVE_INFINITY;
0160:
0161:            /**
0162:             * Maximum <var>x</var> coordinate permitted for the rectangle. The default
0163:             * value is {@link java.lang.Double#POSITIVE_INFINITY}.
0164:             */
0165:            private double xmax = Double.POSITIVE_INFINITY;
0166:
0167:            /**
0168:             * Maximum <var>y</var> coordinate permitted for the rectangle. The default
0169:             * value is {@link java.lang.Double#POSITIVE_INFINITY}.
0170:             */
0171:            private double ymax = Double.POSITIVE_INFINITY;
0172:
0173:            /**
0174:             * The rectangle to control.  The coordinates of this rectangle must be logical coordinates
0175:             * (for example, coordinates in metres), and not screen pixel coordinates. An empty rectangle
0176:             * means that no region is currently selected.
0177:             */
0178:            private final RectangularShape logicalShape;
0179:
0180:            /**
0181:             * Rectangle to be drawn in the component.  This rectange can be different to
0182:             * {@link #logicalShape} and the latter is so small that it is preferable to draw it a little
0183:             * bit bigger than the user has requested. In this case, {@code drawnShape} will serve as
0184:             * a temporary rectangle with extended coordinates.
0185:             *
0186:             * Note: this rectangle should be read only, except in the case of 
0187:             * {@link #update} which is the only method permitted to update it.
0188:             */
0189:            private transient RectangularShape drawnShape;
0190:
0191:            /**
0192:             * Affine transform which changes logical coordinates into pixel coordinates. It is guaranteed
0193:             * that no method except {@link #setTransform} will modify this transformation.
0194:             */
0195:            private final AffineTransform transform = new AffineTransform();
0196:
0197:            /**
0198:             * Last <em>relative</em> mouse coordinates. This information is expressed in logical
0199:             * coordinates (according to the {@link #getTransform} inverse affine transform). The
0200:             * coordinates are relative to (<var>x</var>,<var>y</var>) corner of the rectangle.
0201:             */
0202:            private transient double mouseDX, mouseDY;
0203:
0204:            /**
0205:             * {@code x}, {@code y}, {@code width} and {@code height} coordinates of a
0206:             * box which completely encloses {@link #drawnShape}. These coordinates must be expressed in
0207:             * <strong>pixels</strong>. If need be, the affine transform {@link #getTransform} can be used
0208:             * to change pixel coordinates into logical coordinates and vice versa.
0209:             */
0210:            private transient int x, y, width, height;
0211:
0212:            /**
0213:             * Indicates whether the mouse pointer is over the rectangle.
0214:             */
0215:            private transient boolean mouseOverRect;
0216:
0217:            /**
0218:             * Point used internally by certain calculations in order to avoid
0219:             * the frequent creation of several temporary {@link Point2D} objects.
0220:             */
0221:            private final transient Point2D.Double tmp = new Point2D.Double();
0222:
0223:            /**
0224:             * Indicates if the user is currently dragging the rectangle.
0225:             * For this field to become {@code true}, the mouse must
0226:             * have been over the rectangle as the user pressed the mouse button.
0227:             */
0228:            private transient boolean isDragging;
0229:
0230:            /**
0231:             * Indicates which edges the user is currently adjusting with the mouse.
0232:             * This field is often identical to {@link #adjustingSides}. However,
0233:             * unlike {@link #adjustingSides}, it designates an edge of the shape
0234:             * {@link #logicalShape} and not an edge of the shape in pixels appearing
0235:             * on the screen. It is different, for example, if the affine transform
0236:             * {@link #transform} contains a 90° rotation.
0237:             */
0238:            private transient int adjustingLogicalSides;
0239:
0240:            /**
0241:             * Indicates which edges the user is currently adjusting with the mouse.
0242:             * Permitted values are binary combinations of {@link #NORTH},
0243:             * {@link #SOUTH}, {@link #EAST} and {@link #WEST}.
0244:             */
0245:            private transient int adjustingSides;
0246:
0247:            /**
0248:             * Indicates which edges are allowed to be adjusted.  Permitted
0249:             * values are binary combinations of {@link #NORTH},
0250:             * {@link #SOUTH}, {@link #EAST} and {@link #WEST}.
0251:             */
0252:            private int adjustableSides;
0253:
0254:            /**
0255:             * Indicates if the geometric shape can be moved.
0256:             */
0257:            private boolean moveable = true;
0258:
0259:            /**
0260:             * When the position of the left or right-hand edge of the rectangle
0261:             * is manually edited, this indicates whether the position of the
0262:             * opposite edge should be automatically adjusted.  The default value is
0263:             * {@code false}.
0264:             */
0265:            private boolean synchronizeX;
0266:
0267:            /**
0268:             * When the position of the top or bottom edge of the rectangle is
0269:             * manually edited, this indicates whether the position of the 
0270:             * opposite edge should be automatically adjusted.  The default value is
0271:             * {@code false}.
0272:             */
0273:            private boolean synchronizeY;
0274:
0275:            /** Bit representing north. */
0276:            private static final int NORTH = 1;
0277:            /** Bit representing south. */
0278:            private static final int SOUTH = 2;
0279:            /** Bit representing east.  */
0280:            private static final int EAST = 4;
0281:            /** Bit representing west.  */
0282:            private static final int WEST = 8;
0283:
0284:            /**
0285:             * Cursor codes corresponding to a given {@link adjustingSides} value.
0286:             */
0287:            private static final int[] CURSORS = new int[] {
0288:                    Cursor.MOVE_CURSOR, // 0000 =       |      |       |
0289:                    Cursor.N_RESIZE_CURSOR, // 0001 =       |      |       | NORTH
0290:                    Cursor.S_RESIZE_CURSOR, // 0010 =       |      | SOUTH |
0291:                    Cursor.DEFAULT_CURSOR, // 0011 =       |      | SOUTH | NORTH
0292:                    Cursor.E_RESIZE_CURSOR, // 0100 =       | EAST |       |
0293:                    Cursor.NE_RESIZE_CURSOR, // 0101 =       | EAST |       | NORTH
0294:                    Cursor.SE_RESIZE_CURSOR, // 0110 =       | EAST | SOUTH |
0295:                    Cursor.DEFAULT_CURSOR, // 0111 =       | EAST | SOUTH | NORTH
0296:                    Cursor.W_RESIZE_CURSOR, // 1000 =  WEST |      |       |
0297:                    Cursor.NW_RESIZE_CURSOR, // 1001 =  WEST |      |       | NORTH
0298:                    Cursor.SW_RESIZE_CURSOR // 1010 =  WEST |      | SOUTH |
0299:            };
0300:
0301:            /**
0302:             * Lookup table which converts <i>Swing</i> constants into
0303:             * combinations of {@link #NORTH}, {@link #SOUTH},
0304:             * {@link #EAST} and {@link #WEST} constants. We cannot use
0305:             * <i>Swing</i> constants directly because, unfortunately, they do
0306:             * not correspond to the binary combinations of the four
0307:             * cardinal corners.
0308:             */
0309:            private static final int[] SWING_TO_CUSTOM = new int[] {
0310:                    SwingConstants.NORTH, NORTH, SwingConstants.SOUTH, SOUTH,
0311:                    SwingConstants.EAST, EAST, SwingConstants.WEST, WEST,
0312:                    SwingConstants.NORTH_EAST, NORTH | EAST,
0313:                    SwingConstants.SOUTH_EAST, SOUTH | EAST,
0314:                    SwingConstants.NORTH_WEST, NORTH | WEST,
0315:                    SwingConstants.SOUTH_WEST, SOUTH | WEST };
0316:
0317:            /**
0318:             * List of text fields which represent the coordinates of the
0319:             * rectangle's edges.
0320:             */
0321:            private Control[] editors;
0322:
0323:            /**
0324:             * Constructs an object capable of moving and resizing a rectangular
0325:             * shape through mouse movements. The rectangle will be positioned, by
0326:             * default at the coordinates (0,0).  Its width and height will be null.
0327:             */
0328:            public MouseReshapeTracker() {
0329:                this (new Rectangle2D.Double());
0330:            }
0331:
0332:            /**
0333:             * Constructs an object capable of moving and resizing a rectangular shape
0334:             * through mouse movements.
0335:             *
0336:             * @param shape Rectangular geometric shape. This shape does not have to be
0337:             *        a rectangle.  It could, for example, be a circle.  The
0338:             *        coordinates of this shape will be the initial coordinates of the
0339:             *        visor. They are logical coordinates and not pixel coordinates
0340:             *        Note that the constructor retains a direct reference to this
0341:             *        shape, without creating a clone.  As a consequence, any 
0342:             *        modification carried out on the geometric shape will have
0343:             *        repercussions for this objet {@code MouseReshapeTracker}
0344:             *        and vice versa.
0345:             */
0346:            public MouseReshapeTracker(final RectangularShape shape) {
0347:                this .logicalShape = shape;
0348:                this .drawnShape = shape;
0349:                update();
0350:            }
0351:
0352:            /**
0353:             * Method called automatically after reading this object
0354:             * in order to finish the construction of certain fields.
0355:             */
0356:            private void readObject(final ObjectInputStream in)
0357:                    throws IOException, ClassNotFoundException {
0358:                in.defaultReadObject();
0359:                drawnShape = logicalShape;
0360:                update();
0361:            }
0362:
0363:            /**
0364:             * Updates the internal fields of this object. The adjusted fields will be:
0365:             *
0366:             * <ul>
0367:             *   <li>{@link #drawnShape} for the rectangle to be drawn.</li>
0368:             *   <li>{@link #x}, {@link #y}, {@link #width} and {@link #height}
0369:             *       for the pixel coordinates of {@link #drawnShape}.</li>
0370:             * </ul>
0371:             */
0372:            private void update() {
0373:                /*
0374:                 * Takes into account cases where the affine transform
0375:                 * contains a rotation of 90° or any other.
0376:                 */
0377:                adjustingLogicalSides = inverseTransform(adjustingSides);
0378:                /*
0379:                 * Obtains the geometric shape to draw.  Normally it will be a 
0380:                 * {@link #logicalShape}, except if the latter is so small that we
0381:                 * have considered it preferable to create a temporary shape which
0382:                 * will be slightly bigger.
0383:                 */
0384:                tmp.x = logicalShape.getWidth();
0385:                tmp.y = logicalShape.getHeight();
0386:                transform.deltaTransform(tmp, tmp);
0387:                if (Math.abs(tmp.x) < MIN_WIDTH || Math.abs(tmp.y) < MIN_HEIGHT) {
0388:                    if (Math.abs(tmp.x) < MIN_WIDTH)
0389:                        tmp.x = (tmp.x < 0) ? -MIN_WIDTH : MIN_WIDTH;
0390:                    if (Math.abs(tmp.y) < MIN_HEIGHT)
0391:                        tmp.y = (tmp.y < 0) ? -MIN_HEIGHT : MIN_HEIGHT;
0392:                    try {
0393:                        XAffineTransform.inverseDeltaTransform(transform, tmp,
0394:                                tmp);
0395:                        double x = logicalShape.getX();
0396:                        double y = logicalShape.getY();
0397:                        if ((adjustingLogicalSides & WEST) != 0) {
0398:                            x += logicalShape.getWidth() - tmp.x;
0399:                        }
0400:                        if ((adjustingLogicalSides & NORTH) != 0) {
0401:                            y += logicalShape.getHeight() - tmp.y;
0402:                        }
0403:                        if (drawnShape == logicalShape) {
0404:                            drawnShape = (RectangularShape) logicalShape
0405:                                    .clone();
0406:                        }
0407:                        drawnShape.setFrame(x, y, tmp.x, tmp.y);
0408:                    } catch (NoninvertibleTransformException exception) {
0409:                        drawnShape = logicalShape;
0410:                    }
0411:                } else {
0412:                    drawnShape = logicalShape;
0413:                }
0414:                /*
0415:                 * NOTE: the condition 'drawnShape==logicalShape' indicates that it has
0416:                 *       not been necessary to modify the shape.  The method
0417:                 *      'mouseDragged' will use this information.
0418:                 *      
0419:                 * Now retains the pixel coordinates of the new position of the 
0420:                 * rectangle.
0421:                 */
0422:                double xmin = Double.POSITIVE_INFINITY;
0423:                double ymin = Double.POSITIVE_INFINITY;
0424:                double xmax = Double.NEGATIVE_INFINITY;
0425:                double ymax = Double.NEGATIVE_INFINITY;
0426:                for (int i = 0; i < 4; i++) {
0427:                    tmp.x = (i & 1) == 0 ? drawnShape.getMinX() : drawnShape
0428:                            .getMaxX();
0429:                    tmp.y = (i & 2) == 0 ? drawnShape.getMinY() : drawnShape
0430:                            .getMaxY();
0431:                    transform.transform(tmp, tmp);
0432:                    if (tmp.x < xmin) {
0433:                        xmin = tmp.x;
0434:                    }
0435:                    if (tmp.x > xmax) {
0436:                        xmax = tmp.x;
0437:                    }
0438:                    if (tmp.y < ymin) {
0439:                        ymin = tmp.y;
0440:                    }
0441:                    if (tmp.y > ymax) {
0442:                        ymax = tmp.y;
0443:                    }
0444:                }
0445:                x = (int) Math.floor(xmin) - 1;
0446:                y = (int) Math.floor(ymin) - 1;
0447:                width = (int) Math.ceil(xmax - xmin) + 2;
0448:                height = (int) Math.ceil(ymax - ymin) + 2;
0449:            }
0450:
0451:            /**
0452:             * Returns the transform of {@code adjusting}.
0453:             * @param adjusting to transform (generally {@link #adjustingSides}).
0454:             */
0455:            private int inverseTransform(int adjusting) {
0456:                switch (adjusting & (WEST | EAST)) {
0457:                case WEST:
0458:                    tmp.x = -1;
0459:                    break;
0460:                case EAST:
0461:                    tmp.x = +1;
0462:                    break;
0463:                default:
0464:                    tmp.x = 0;
0465:                    break;
0466:                }
0467:                switch (adjusting & (NORTH | SOUTH)) {
0468:                case NORTH:
0469:                    tmp.y = -1;
0470:                    break;
0471:                case SOUTH:
0472:                    tmp.y = +1;
0473:                    break;
0474:                default:
0475:                    tmp.y = 0;
0476:                    break;
0477:                }
0478:                try {
0479:                    XAffineTransform.inverseDeltaTransform(transform, tmp, tmp);
0480:                    final double normalize = 0.25 * XMath.hypot(tmp.x, tmp.y);
0481:                    tmp.x /= normalize;
0482:                    tmp.y /= normalize;
0483:                    adjusting = 0;
0484:                    switch (XMath.sgn(Math.rint(tmp.x))) {
0485:                    case -1:
0486:                        adjusting |= WEST;
0487:                        break;
0488:                    case +1:
0489:                        adjusting |= EAST;
0490:                        break;
0491:                    }
0492:                    switch (XMath.sgn(Math.rint(tmp.y))) {
0493:                    case -1:
0494:                        adjusting |= NORTH;
0495:                        break;
0496:                    case +1:
0497:                        adjusting |= SOUTH;
0498:                        break;
0499:                    }
0500:                    return adjusting;
0501:                } catch (NoninvertibleTransformException exception) {
0502:                    return adjusting;
0503:                }
0504:            }
0505:
0506:            /**
0507:             * Declares the affine transform which will transform the logical
0508:             * coordinates into pixel coordinates.  This is the affine transform
0509:             * specified in {@link java.awt.Graphics2D#transform} at the moment that
0510:             * {@code this} is drawn. The information contained in this affine
0511:             * transform is necessary for several of this class's methods to work.
0512:             * It is the programmer's responsability to ensure that this
0513:             * information is always up-to-date.  By default,
0514:             * {@code MouseReshapeTracker} uses an identity transform.
0515:             */
0516:            public void setTransform(final AffineTransform newTransform) {
0517:                if (!this .transform.equals(newTransform)) {
0518:                    fireStateWillChange();
0519:                    this .transform.setTransform(newTransform);
0520:                    update();
0521:                    fireStateChanged();
0522:                }
0523:            }
0524:
0525:            /**
0526:             * Returns the position and the bounds of the rectangle.  These
0527:             * bounds can be slightly bigger than those returned by 
0528:             * {@link #getFrame} since {@code getBounds2D()} returns the
0529:             * bounds of the rectangle visible on screen, which can have certain
0530:             * minimum bounds.
0531:             */
0532:            public Rectangle getBounds() {
0533:                return drawnShape.getBounds();
0534:            }
0535:
0536:            /**
0537:             * Returns the position and the bounds of the rectangle.  These
0538:             * bounds can be slightly bigger than those returned by 
0539:             * {@link #getFrame} since {@code getBounds2D()} returns the
0540:             * bounds of the rectangle visible on screen, which can have certain
0541:             * minimum bounds.
0542:             */
0543:            public Rectangle2D getBounds2D() {
0544:                return drawnShape.getBounds2D();
0545:            }
0546:
0547:            /**
0548:             * Returns the position and the bounds of the rectangle.
0549:             * This information is expressed in logical coordinates.
0550:             *
0551:             * @see #getCenterX
0552:             * @see #getCenterY
0553:             * @see #getMinX
0554:             * @see #getMaxX
0555:             * @see #getMinY
0556:             * @see #getMaxY
0557:             */
0558:            public Rectangle2D getFrame() {
0559:                return logicalShape.getFrame();
0560:            }
0561:
0562:            /**
0563:             * Defines a new position and bounds for the rectangle.  The coordinates
0564:             * passed to this method should be logical coordinates rather than pixel
0565:             * coordinates.  If the range of values covered by the rectangle is
0566:             * limited by a call to {@link #setClip}, the rectangle will be
0567:             * moved and resized as needed to fit into the permitted region.
0568:             *
0569:             * @return {@code true} if the rectangle's coordinates have changed.
0570:             *
0571:             * @see #getFrame
0572:             */
0573:            public final boolean setFrame(final Rectangle2D frame) {
0574:                return setFrame(frame.getX(), frame.getY(), frame.getWidth(),
0575:                        frame.getHeight());
0576:            }
0577:
0578:            /**
0579:             * Defines a new position and bounds for the rectangle.  The coordinates
0580:             * passed to this method should be logical coordinates rather than pixel
0581:             * coordinates.  If the range of values covered by the rectangle is
0582:             * limited by a call to {@link #setClip}, the rectangle will be
0583:             * moved and resized as needed to fit into the permitted region.
0584:             *
0585:             * @return {@code true} if the rectangle's coordinates have changed.
0586:             *
0587:             * @see #setX
0588:             * @see #setY
0589:             */
0590:            public boolean setFrame(double x, double y, double width,
0591:                    double height) {
0592:                final double oldX = logicalShape.getX();
0593:                final double oldY = logicalShape.getY();
0594:                final double oldW = logicalShape.getWidth();
0595:                final double oldH = logicalShape.getHeight();
0596:                if (x < xmin)
0597:                    x = xmin;
0598:                if (y < ymin)
0599:                    y = ymin;
0600:                if (x + width > xmax) {
0601:                    x = Math.max(xmin, xmax - width);
0602:                    width = xmax - x;
0603:                }
0604:                if (y + height > ymax) {
0605:                    y = Math.max(ymin, ymax - height);
0606:                    height = ymax - y;
0607:                }
0608:                fireStateWillChange();
0609:                logicalShape.setFrame(x, y, width, height);
0610:                if (oldX != logicalShape.getX() || oldY != logicalShape.getY()
0611:                        || oldW != logicalShape.getWidth()
0612:                        || oldH != logicalShape.getHeight()) {
0613:                    update();
0614:                    fireStateChanged();
0615:                    return true;
0616:                }
0617:                return false;
0618:            }
0619:
0620:            /**
0621:             * Defines the new range of values covered by the rectangle according to
0622:             * the <var>x</var> axis. The values covered along the <var>y</var> axis
0623:             * will not be changed. The values must be expressed in logical coordinates
0624:             *
0625:             * @see #getMinX
0626:             * @see #getMaxX
0627:             * @see #getCenterX
0628:             */
0629:            public final void setX(final double min, final double max) {
0630:                setFrame(Math.min(min, max), logicalShape.getY(), Math.abs(max
0631:                        - min), logicalShape.getHeight());
0632:            }
0633:
0634:            /**
0635:             * Defines the new range of values covered by the rectangle according to
0636:             * the <var>y</var> axis. The values covered along the <var>x</var> axis
0637:             * will not be changed. The values must be expressed in logical coordinates
0638:             *
0639:             * @see #getMinY
0640:             * @see #getMaxY
0641:             * @see #getCenterY
0642:             */
0643:            public final void setY(final double min, final double max) {
0644:                setFrame(logicalShape.getX(), Math.min(min, max), logicalShape
0645:                        .getWidth(), Math.abs(max - min));
0646:            }
0647:
0648:            /**
0649:             * Returns the minimum <var>x</var> coordinate of the rectangle
0650:             * (the logical coordinate, not the pixel coordinate).
0651:             */
0652:            public double getMinX() {
0653:                return logicalShape.getMinX();
0654:            }
0655:
0656:            /**
0657:             * Returns the minimum <var>y</var> coordinate of the rectangle
0658:             * (the logical coordinate, not the pixel coordinate).
0659:             */
0660:            public double getMinY() {
0661:                return logicalShape.getMinY();
0662:            }
0663:
0664:            /**
0665:             * Returns the maximum <var>x</var> coordinate of the rectangle
0666:             * (the logical coordinate, not the pixel coordinate).
0667:             */
0668:            public double getMaxX() {
0669:                return logicalShape.getMaxX();
0670:            }
0671:
0672:            /**
0673:             * Returns the maximum <var>y</var> coordinate of the rectangle
0674:             * (the logical coordinate, not the pixel coordinate).
0675:             */
0676:            public double getMaxY() {
0677:                return logicalShape.getMaxY();
0678:            }
0679:
0680:            /**
0681:             * Returns the width of the rectangle. This width is expressed
0682:             * in logical coordinates, not pixel coordinates.
0683:             */
0684:            public double getWidth() {
0685:                return logicalShape.getWidth();
0686:            }
0687:
0688:            /**
0689:             * Returns the height of the rectangle.  This height is expressed
0690:             * in logical coordinates, not pixel coordinates.
0691:             */
0692:            public double getHeight() {
0693:                return logicalShape.getHeight();
0694:            }
0695:
0696:            /**
0697:             * Returns the <var>x</var> coordinate of the centre of the rectangle
0698:             * (logical coordinate, not pixel coordinate).
0699:             */
0700:            public double getCenterX() {
0701:                return logicalShape.getCenterX();
0702:            }
0703:
0704:            /**
0705:             * Returns the <var>y</var> coordinate of the centre of the rectangle
0706:             * (logical coordinate, not pixel coordinate).
0707:             */
0708:            public double getCenterY() {
0709:                return logicalShape.getCenterY();
0710:            }
0711:
0712:            /**
0713:             * Indicates whether the rectangle is empty.  This will be
0714:             * the case if the width and / or height is null.
0715:             */
0716:            public boolean isEmpty() {
0717:                return logicalShape.isEmpty();
0718:            }
0719:
0720:            /**
0721:             * Indicates whether the rectangular shape contains the specified point.
0722:             * This point should be expressed in logical coordinates.
0723:             */
0724:            public boolean contains(final Point2D point) {
0725:                return logicalShape.contains(point);
0726:            }
0727:
0728:            /**
0729:             * Indicates whether the rectangular shape contains the specified point.
0730:             * This point should be expressed in logical coordinates.
0731:             */
0732:            public boolean contains(final double x, final double y) {
0733:                return logicalShape.contains(x, y);
0734:            }
0735:
0736:            /**
0737:             * Indicates whether the rectangular shape contains the specified
0738:             * rectangle.  This rectangle should be expressed in logical
0739:             * coordinates.  This method can conservatively return
0740:             * {@code false} as permitted by the {@link Shape} specification.
0741:             */
0742:            public boolean contains(final Rectangle2D rect) {
0743:                return logicalShape.contains(rect);
0744:            }
0745:
0746:            /**
0747:             * Indicates whether the rectangular shape contains the specified
0748:             * rectangle.  This rectangle must be expressed in logical
0749:             * coordinates.  This method can conservatively return
0750:             * {@code false} as permitted by the {@link Shape} specification.
0751:             */
0752:            public boolean contains(double x, double y, double width,
0753:                    double height) {
0754:                return logicalShape.contains(x, y, width, height);
0755:            }
0756:
0757:            /**
0758:             * Indicates whether the rectangular shape intersects the specified
0759:             * rectangle.  This rectangle must be expressed in logical coordinates.
0760:             * This method can conservatively return {@code true} as permitted by
0761:             * the {@link Shape} specification.
0762:             */
0763:            public boolean intersects(final Rectangle2D rect) {
0764:                return drawnShape.intersects(rect);
0765:            }
0766:
0767:            /**
0768:             * Indicates whether the rectangular shape intersects the specified
0769:             * rectangle.  This rectangle must be expressed in logical coordinates.
0770:             * This method can conservatively return {@code true} as permitted by
0771:             * the {@link Shape} specification.
0772:             */
0773:            public boolean intersects(double x, double y, double width,
0774:                    double height) {
0775:                return drawnShape.intersects(x, y, width, height);
0776:            }
0777:
0778:            /**
0779:             * Returns a path iterator for the rectangular shape to be drawn.
0780:             */
0781:            public PathIterator getPathIterator(final AffineTransform transform) {
0782:                return drawnShape.getPathIterator(transform);
0783:            }
0784:
0785:            /**
0786:             * Returns a path iterator for the rectangular shape to be drawn.
0787:             */
0788:            public PathIterator getPathIterator(
0789:                    final AffineTransform transform, final double flatness) {
0790:                return drawnShape.getPathIterator(transform, flatness);
0791:            }
0792:
0793:            /**
0794:             * Returns the bounds between which the rectangle can move.
0795:             * These bounds are specified in logical coordinates.
0796:             */
0797:            public Rectangle2D getClip() {
0798:                return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax
0799:                        - ymin);
0800:            }
0801:
0802:            /**
0803:             * Defines the bounds between which the rectangle can move.
0804:             * This method manages infinities correctly if the specified
0805:             * rectangle has redefined its {@code getMaxX()}
0806:             * and {@code getMaxY()} methods correctly.
0807:             *
0808:             * @see #setClipMinMax
0809:             */
0810:            public final void setClip(final Rectangle2D rect) {
0811:                setClipMinMax(rect.getMinX(), rect.getMaxX(), rect.getMinY(),
0812:                        rect.getMaxY());
0813:            }
0814:
0815:            /**
0816:             * Defines the bounds between which the rectangle can move.  This method
0817:             * simply calls {@link #setClipMinMax setClipMinMax(...)} with the
0818:             * appropriate parameters.  It is defined in order to avoid confusion
0819:             * amongst programmers used to <em>Java2D</em> conventions. If you want to
0820:             * specify infinite values (in order to widen the visor's bounds to all
0821:             * possible values along certain axes), you <u>must</u> use
0822:             * {@link #setClipMinMax setClipMinMax(...)} rather than 
0823:             * {@code setClip(...)}.
0824:             */
0825:            public final void setClip(final double x, final double y,
0826:                    final double width, final double height) {
0827:                setClipMinMax(x, x + width, y, y + height);
0828:            }
0829:
0830:            /**
0831:             * Defines the bounds between which the rectangle can move.  Note that this
0832:             * method's arguments don't correspond to the normal arguments of 
0833:             * {@link java.awt.geom.Rectangle2D}. <em>Java2D</em> convention demands
0834:             * that we specify a rectangle using a quadruplet 
0835:             * ({@code x},{@code y},{@code width},{@code height})
0836:             * However, this is a bad choice in the context of almost all the methods
0837:             * in our library.  As well as complicating most calculations (if you need
0838:             * convincing, just count the number of occurrences of the expression
0839:             * {@code x+width} even in the geometric classes of <em>Java2D</em>),
0840:             * it is incapable of correctly representing a rectangle which has one or
0841:             * more coordinates stretching to infinity.  A better convention would
0842:             * have been to use the minimum and maximum values according to
0843:             * <var>x</var> and <var>y</var>, as this method does.
0844:             * <br><br>
0845:             * This method's arguments define the minimum and maximum values that the
0846:             * logical coordinates of the rectangle can take.
0847:             * The values {@link java.lang.Double#NEGATIVE_INFINITY} and
0848:             * {@link java.lang.Double#POSITIVE_INFINITY} are valid for indicating
0849:             * that the visor can extend across all values according to certain axes.
0850:             * The value {@link java.lang.Double#NaN} for a given argument indicates
0851:             * that we want to keep the old value.  If the visor doesn't fit
0852:             * completely within the new bounds, it will be moved and resized as needed
0853:             * in order to make it fit.
0854:             */
0855:            public void setClipMinMax(double xmin, double xmax, double ymin,
0856:                    double ymax) {
0857:                if (xmin > xmax) {
0858:                    final double tmp = xmin;
0859:                    xmin = xmax;
0860:                    xmax = tmp;
0861:                }
0862:                if (ymin > ymax) {
0863:                    final double tmp = ymin;
0864:                    ymin = ymax;
0865:                    ymax = tmp;
0866:                }
0867:                if (!Double.isNaN(xmin)) {
0868:                    this .xmin = xmin;
0869:                }
0870:                if (!Double.isNaN(xmax)) {
0871:                    this .xmax = xmax;
0872:                }
0873:                if (!Double.isNaN(ymin)) {
0874:                    this .ymin = ymin;
0875:                }
0876:                if (!Double.isNaN(ymax)) {
0877:                    this .ymax = ymax;
0878:                }
0879:                setFrame(logicalShape.getX(), logicalShape.getY(), logicalShape
0880:                        .getWidth(), logicalShape.getHeight());
0881:            }
0882:
0883:            /**
0884:             * Method called automatically when a change in the clip is required.
0885:             * This method can be called, for example, when the user manually edits
0886:             * the position of the rectangle in a text field, and the new position
0887:             * falls outside the current clip.  This method does <u>not</u> have to 
0888:             * accept a clip change.  It can do nothing, which is the same as
0889:             * refusing any change.  It can also always unconditionally accept any
0890:             * change by calling {@link #setClipMinMax}.  Finally, it can reach a 
0891:             * compromise solution by imposing certain conditions on the changes.
0892:             * The default implementation does nothing, which means that no 
0893:             * automatic change in the clip will be authorised.
0894:             */
0895:            protected void clipChangeRequested(double xmin, double xmax,
0896:                    double ymin, double ymax) {
0897:            }
0898:
0899:            /**
0900:             * Indicates whether the rectangle can be moved with the mouse. By default,
0901:             * it can be moved but not resized.
0902:             */
0903:            public boolean isMoveable() {
0904:                return moveable;
0905:            }
0906:
0907:            /**
0908:             * Specifies whether the rectangle can be moved with the mouse.  The value
0909:             * {@code false} indicates that the rectangle cannot be moved, but can
0910:             * still be resized if {@link #setAdjustable} has been called with the 
0911:             * appropriate parameters.
0912:             */
0913:            public void setMoveable(final boolean moveable) {
0914:                this .moveable = moveable;
0915:            }
0916:
0917:            /**
0918:             * Indicates whether the size of a rectangle can be modified using
0919:             * a specified edge.  The specified edge must be one of the following
0920:             * constants:
0921:             *
0922:             * <table border align=center cellpadding=8 bgcolor=floralwhite>
0923:             * <tr><td>{@link SwingConstants#NORTH_WEST}</td><td>{@link SwingConstants#NORTH}</td><td>{@link SwingConstants#NORTH_EAST}</td></tr>
0924:             * <tr><td>{@link SwingConstants#WEST      }</td><td>                            </td><td>{@link SwingConstants#EAST      }</td></tr>
0925:             * <tr><td>{@link SwingConstants#SOUTH_WEST}</td><td>{@link SwingConstants#SOUTH}</td><td>{@link SwingConstants#SOUTH_EAST}</td></tr>
0926:             * </table>
0927:             *
0928:             * These constants designate the edge which is visible on screen. For
0929:             * example, {@code NORTH} always designates the top edge on the
0930:             * screen.  However, this could correspond to another edge of the logical
0931:             * shape {@code this} depending on the affine transform which was 
0932:             * specified during the last call to {@link #setTransform}. For example,
0933:             * {@code AffineTransform.getScaleInstance(+1,-1)} has the effect of
0934:             * inverting the y axis so that the <var>y</var><sub>max</sub> values
0935:             * appear to the North rather than the <var>y</var><sub>min</sub> values.
0936:             */
0937:            public boolean isAdjustable(int side) {
0938:                side = convertSwingConstant(side);
0939:                return (adjustableSides & side) == side;
0940:            }
0941:
0942:            /**
0943:             * Specifies whether the size of the rectangle can be modified using the
0944:             * specified edge.  The specified edge must be one of the following
0945:             * constants:
0946:             *
0947:             * <table border align=center cellpadding=8 bgcolor=floralwhite>
0948:             * <tr><td>{@link SwingConstants#NORTH_WEST}</td><td>{@link SwingConstants#NORTH}</td><td>{@link SwingConstants#NORTH_EAST}</td></tr>
0949:             * <tr><td>{@link SwingConstants#WEST      }</td><td>                            </td><td>{@link SwingConstants#EAST      }</td></tr>
0950:             * <tr><td>{@link SwingConstants#SOUTH_WEST}</td><td>{@link SwingConstants#SOUTH}</td><td>{@link SwingConstants#SOUTH_EAST}</td></tr>
0951:             * </table>
0952:             *
0953:             * These constants designate the edge which is visible on screen. For
0954:             * example, {@code NORTH} always designates the top edge on the
0955:             * screen.  However, this could correspond to another edge of the logical
0956:             * shape {@code this} depending on the affine transform which was 
0957:             * specified during the last call to {@link #setTransform}. For example,
0958:             * {@code AffineTransform.getScaleInstance(+1,-1)} has the effect of
0959:             * inverting the y axis so that the <var>y</var><sub>max</sub> values
0960:             * appear to the North rather than the <var>y</var><sub>min</sub> values.
0961:             */
0962:            public void setAdjustable(int side, final boolean adjustable) {
0963:                side = convertSwingConstant(side);
0964:                if (adjustable) {
0965:                    adjustableSides |= side;
0966:                } else {
0967:                    adjustableSides &= ~side;
0968:                }
0969:            }
0970:
0971:            /*
0972:             * Converts a Swing edge constant to system used by this package.
0973:             * We cannot use <i>Swing</i> constants directly because,
0974:             * unfortunately, they do not correspond to the binary combinations of the
0975:             * four cardinal corners.
0976:             */
0977:            private int convertSwingConstant(final int side) {
0978:                for (int i = 0; i < SWING_TO_CUSTOM.length; i += 2) {
0979:                    if (SWING_TO_CUSTOM[i] == side) {
0980:                        return SWING_TO_CUSTOM[i + 1];
0981:                    }
0982:                }
0983:                throw new IllegalArgumentException(String.valueOf(side));
0984:            }
0985:
0986:            /**
0987:             * Method called automatically during mouse movements.  The default
0988:             * implementation checks whether the cursor is inside the rectangle or on
0989:             * one of its edges, and adjusts the mouse pointer icon accordingly.
0990:             */
0991:            public void mouseMoved(final MouseEvent event) {
0992:                if (!isDragging) {
0993:                    final Component source = event.getComponent();
0994:                    if (source != null) {
0995:                        int x = event.getX();
0996:                        tmp.x = x;
0997:                        int y = event.getY();
0998:                        tmp.y = y;
0999:                        final boolean mouseOverRect;
1000:                        try {
1001:                            mouseOverRect = drawnShape.contains(transform
1002:                                    .inverseTransform(tmp, tmp));
1003:                        } catch (NoninvertibleTransformException exception) {
1004:                            // Ignore this exception.
1005:                            return;
1006:                        }
1007:                        final boolean mouseOverRectChanged = (mouseOverRect != this .mouseOverRect);
1008:                        if (mouseOverRect) {
1009:                            /*
1010:                             * We do not use "adjustingLogicalSides" because we are working
1011:                             * with pixel coordinates and not logical coordinates.
1012:                             */
1013:                            final int old = adjustingSides;
1014:                            adjustingSides = 0;
1015:                            if (Math.abs(x -= this .x) <= RESIZE_POS) {
1016:                                adjustingSides |= WEST;
1017:                            }
1018:                            if (Math.abs(y -= this .y) <= RESIZE_POS) {
1019:                                adjustingSides |= NORTH;
1020:                            }
1021:                            if (Math.abs(x - this .width) <= RESIZE_POS) {
1022:                                adjustingSides |= EAST;
1023:                            }
1024:                            if (Math.abs(y - this .height) <= RESIZE_POS) {
1025:                                adjustingSides |= SOUTH;
1026:                            }
1027:
1028:                            adjustingSides &= adjustableSides;
1029:                            if (adjustingSides != old || mouseOverRectChanged) {
1030:                                if (adjustingSides == 0 && !moveable) {
1031:                                    source.setCursor(null);
1032:                                } else {
1033:                                    adjustingLogicalSides = inverseTransform(adjustingSides);
1034:                                    source
1035:                                            .setCursor(Cursor
1036:                                                    .getPredefinedCursor(adjustingSides < CURSORS.length ? CURSORS[adjustingSides]
1037:                                                            : Cursor.DEFAULT_CURSOR));
1038:                                }
1039:                            }
1040:                            if (mouseOverRectChanged) {
1041:                                // Adding and removing listeners worked well, but had 
1042:                                // the disadvantage of changing the order of the
1043:                                // listeners. This caused problems when the order was
1044:                                // important.
1045:
1046:                                //source.addMouseListener(this);
1047:                                this .mouseOverRect = mouseOverRect;
1048:                            }
1049:                        } else if (mouseOverRectChanged) {
1050:                            adjustingSides = 0;
1051:                            source.setCursor(null);
1052:                            //source.removeMouseListener(this);
1053:                            this .mouseOverRect = mouseOverRect;
1054:                        }
1055:                    }
1056:                }
1057:            }
1058:
1059:            /**
1060:             * Method called automatically when the user presses a mouse button 
1061:             * anywhere within the component.  The default implementation
1062:             * checks if the button was pressed whilst the mouse cursor was
1063:             * within the rectangle.  If so, this object will track the mouse drags
1064:             * to move or resize the rectangle.
1065:             */
1066:            public void mousePressed(final MouseEvent e) {
1067:                if (!e.isConsumed()
1068:                        && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
1069:                    if (adjustingSides != 0 || moveable) {
1070:                        tmp.x = e.getX();
1071:                        tmp.y = e.getY();
1072:                        try {
1073:                            if (drawnShape.contains(transform.inverseTransform(
1074:                                    tmp, tmp))) {
1075:                                mouseDX = tmp.x - drawnShape.getX();
1076:                                mouseDY = tmp.y - drawnShape.getY();
1077:                                isDragging = true;
1078:                                e.consume();
1079:                            }
1080:                        } catch (NoninvertibleTransformException exception) {
1081:                            // Pas besoin de gérer cette exception.
1082:                            // L'ignorer est correct.
1083:                        }
1084:                    }
1085:                }
1086:            }
1087:
1088:            /**
1089:             * Method called automatically during mouse drags.  The default
1090:             * implementation applies the mouse movement to the rectangle and notifies
1091:             * the component where the event which it needs to redraw, at least in
1092:             * part, came from.
1093:             */
1094:            public void mouseDragged(final MouseEvent e) {
1095:                if (isDragging) {
1096:                    final int adjustingLogicalSides = this .adjustingLogicalSides;
1097:                    final Component source = e.getComponent();
1098:                    if (source != null)
1099:                        try {
1100:                            tmp.x = e.getX();
1101:                            tmp.y = e.getY();
1102:                            transform.inverseTransform(tmp, tmp);
1103:                            /*
1104:                             * Calculates the (x0,y0) coordinates of the corner of the
1105:                             * rectangle. The (mouseDX, mouseDY) coordinates represent the
1106:                             * position of the mouse at the moment the button is pressed
1107:                             * and don't normally change (except during certain
1108:                             * adjustments).  In determining (mouseDX, mouseDY), they is
1109:                             * calculated as if the user began to drag the rectangle at
1110:                             * the very corner, though in reality they could have clicked
1111:                             * anywhere.
1112:                             */
1113:                            double x0 = tmp.x - mouseDX;
1114:                            double y0 = tmp.y - mouseDY;
1115:                            double dx = drawnShape.getWidth();
1116:                            double dy = drawnShape.getHeight();
1117:                            final double oldWidth = dx;
1118:                            final double oldHeight = dy;
1119:                            /*
1120:                             * Deals with cases where, instead of dragging the rectangle,
1121:                             * the user is in the process of resizing it.
1122:                             */
1123:                            switch (adjustingLogicalSides & (EAST | WEST)) {
1124:                            case WEST: {
1125:                                if (x0 < xmin) {
1126:                                    x0 = xmin;
1127:                                }
1128:                                dx += drawnShape.getX() - x0;
1129:                                if (!(dx > 0)) {
1130:                                    dx = drawnShape.getWidth();
1131:                                    x0 = drawnShape.getX();
1132:                                }
1133:                                break;
1134:                            }
1135:                            case EAST: {
1136:                                dx += x0 - (x0 = drawnShape.getX());
1137:                                final double limit = xmax - x0;
1138:                                if (dx > limit) {
1139:                                    dx = limit;
1140:                                }
1141:                                if (!(dx > 0)) {
1142:                                    dx = drawnShape.getWidth();
1143:                                    x0 = drawnShape.getX();
1144:                                }
1145:                                break;
1146:                            }
1147:                            }
1148:                            switch (adjustingLogicalSides & (NORTH | SOUTH)) {
1149:                            case NORTH: {
1150:                                if (y0 < ymin) {
1151:                                    y0 = ymin;
1152:                                }
1153:                                dy += drawnShape.getY() - y0;
1154:                                if (!(dy > 0)) {
1155:                                    dy = drawnShape.getHeight();
1156:                                    y0 = drawnShape.getY();
1157:                                }
1158:                                break;
1159:                            }
1160:                            case SOUTH: {
1161:                                dy += y0 - (y0 = drawnShape.getY());
1162:                                final double limit = ymax - y0;
1163:                                if (dy > limit)
1164:                                    dy = limit;
1165:                                if (!(dy > 0)) {
1166:                                    dy = drawnShape.getHeight();
1167:                                    y0 = drawnShape.getY();
1168:                                }
1169:                                break;
1170:                            }
1171:                            }
1172:                            /*
1173:                             * The (x0, y0, dx, dy) coordinates now give the new position
1174:                             * and size of the rectangle. But, before making the change,
1175:                             * check whether only one edge was being adjusted.  If so, we
1176:                             * cancel the changes with respect to the other edge (if not,
1177:                             * the user could move the rectangle vertically at the same
1178:                             * time as adjusting its right or left edge, which is not at
1179:                             * all practical...)
1180:                             */
1181:                            if ((adjustingLogicalSides & (NORTH | SOUTH)) != 0
1182:                                    && (adjustingLogicalSides & (EAST | WEST)) == 0) {
1183:                                x0 = drawnShape.getX();
1184:                                dx = drawnShape.getWidth();
1185:                            }
1186:                            if ((adjustingLogicalSides & (NORTH | SOUTH)) == 0
1187:                                    && (adjustingLogicalSides & (EAST | WEST)) != 0) {
1188:                                y0 = drawnShape.getY();
1189:                                dy = drawnShape.getHeight();
1190:                            }
1191:                            /*
1192:                             * If the user didn't adjusted any side, then make sure
1193:                             * that the logical size is conserved (i.e. discard the
1194:                             * "drawing" size if it was different).
1195:                             */
1196:                            if (adjustingLogicalSides == 0) {
1197:                                final double old_dx = logicalShape.getWidth();
1198:                                final double old_dy = logicalShape.getHeight();
1199:                                x0 += (dx - old_dx) / 2;
1200:                                y0 += (dy - old_dy) / 2;
1201:                                dx = old_dx;
1202:                                dy = old_dy;
1203:                            }
1204:                            /*
1205:                             * Modifies the rectangle's coordinates and signals that the
1206:                             * component needs redrawing.
1207:                             * Note: 'repaint' should be called before and after
1208:                             *       'setFrame' because the coordinates change.
1209:                             */
1210:                            source.repaint(x, y, width, height);
1211:                            try {
1212:                                setFrame(x0, y0, dx, dy);
1213:                            } catch (RuntimeException exception) {
1214:                                exception.printStackTrace();
1215:                            }
1216:                            source.repaint(x, y, width, height);
1217:                            /*
1218:                             * Adjustment for special cases.
1219:                             */
1220:                            if ((adjustingLogicalSides & EAST) != 0) {
1221:                                mouseDX += (drawnShape.getWidth() - oldWidth);
1222:                            }
1223:                            if ((adjustingLogicalSides & SOUTH) != 0) {
1224:                                mouseDY += (drawnShape.getHeight() - oldHeight);
1225:                            }
1226:                        } catch (NoninvertibleTransformException exception) {
1227:                            // Ignore.
1228:                        }
1229:                }
1230:            }
1231:
1232:            /**
1233:             * Method called automatically when the user releases the mouse button.
1234:             * The default implementation calls {@link #stateChanged} with the
1235:             * argument {@code false}, in order to inform the derived classes
1236:             * that the changes are finished.
1237:             */
1238:            public void mouseReleased(final MouseEvent event) {
1239:                if (isDragging
1240:                        && (event.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
1241:                    isDragging = false;
1242:                    final Component source = event.getComponent();
1243:                    try {
1244:                        tmp.x = event.getX();
1245:                        tmp.y = event.getY();
1246:                        mouseOverRect = drawnShape.contains(transform
1247:                                .inverseTransform(tmp, tmp));
1248:                        if (!mouseOverRect && source != null)
1249:                            source.setCursor(null);
1250:                        event.consume();
1251:                    } catch (NoninvertibleTransformException exception) {
1252:                        // Ignore this exception.
1253:                    }
1254:                    try {
1255:                        // It is essential that 'isDragging=false'.
1256:                        fireStateChanged();
1257:                    } catch (RuntimeException exception) {
1258:                        ExceptionMonitor.show(source, exception);
1259:                    }
1260:                }
1261:            }
1262:
1263:            /**
1264:             * Method called automatically <strong>before</strong> the position
1265:             * or the size of the visor has changed. A call to 
1266:             * {@code stateWillChange} is normally followed by a call to
1267:             * {@link #stateChanged}, <u>except</u> if the expected change
1268:             * didn't ultimately occur.  The derived classes can redefine this method
1269:             * to take the necessary actions when a change is on the point of being
1270:             * actioned.  They must not, however, call any method which risks modifying
1271:             * the state of this object.  The default implementation does nothing.
1272:             *
1273:             * @param isAdjusting {@code true} if the user is still
1274:             *        modifying the position of the visor, {@code false}
1275:             *        if they have released the mouse button.
1276:             */
1277:            protected void stateWillChange(final boolean isAdjusting) {
1278:            }
1279:
1280:            /**
1281:             * Method called automatically <strong>after</strong> the position and
1282:             * size of the visor has changed.  The call to {@code stateChanged}
1283:             * must have been preceded by a call to {@link #stateWillChange}. The
1284:             * derived classes can redefine this method to take the necessary
1285:             * actions when a change has just been actioned.  They must not, however,
1286:             * call any method which risks modifying the state of this object.  The
1287:             * default implementation does nothing.
1288:             *
1289:             * @param isAdjusting {@code true} if the user is still
1290:             *        modifying the position of the visor, {@code false}
1291:             *        if they have released the mouse button.
1292:             */
1293:            protected void stateChanged(final boolean isAdjusting) {
1294:            }
1295:
1296:            /**
1297:             * Method called automatically before the position or the
1298:             * size of the visor has changed.
1299:             */
1300:            private void fireStateWillChange() {
1301:                stateWillChange(isDragging);
1302:            }
1303:
1304:            /**
1305:             * Method called automatically after the position or the
1306:             * size of the visor has changed.
1307:             */
1308:            private void fireStateChanged() {
1309:                updateEditors();
1310:                stateChanged(isDragging);
1311:            }
1312:
1313:            /**
1314:             * Updates the text in the editors. Each editor added by the
1315:             * method {@link #addEditor addEditor(...)} will have its
1316:             * text reformatted.  This method can be called, for example,
1317:             * after changing the format used by the editors.  It is not
1318:             * necessary to call this method each time the mouse moves;
1319:             * it is done automatically.
1320:             */
1321:            public void updateEditors() {
1322:                if (editors != null) {
1323:                    for (int i = 0; i < editors.length; i++) {
1324:                        editors[i].updateText();
1325:                    }
1326:                }
1327:            }
1328:
1329:            /**
1330:             * Adds an editor in which the user can explicitly specify the
1331:             * coordinates of one of the edges of the rectangle.  Each time
1332:             * the user drags the rectangle, the text appearing in this editor
1333:             * will automatically be updated.  If the user explicitly enters
1334:             * a new value in this editor, the position of the rectangle will be
1335:             * adjusted.
1336:             *
1337:             * @param format    Format to use for writing and interpreting the values
1338:             *                  in the editor.
1339:             * @param side      Edge of the rectangle whose coordinates will be
1340:             *                  controlled by the editor.  It should be one of the
1341:             *                  following constants:
1342:             *
1343:             * <table border align=center cellpadding=8 bgcolor=floralwhite>
1344:             * <tr><td>{@link SwingConstants#NORTH_WEST}</td><td>{@link SwingConstants#NORTH}</td><td>{@link SwingConstants#NORTH_EAST}</td></tr>
1345:             * <tr><td>{@link SwingConstants#WEST      }</td><td>                            </td><td>{@link SwingConstants#EAST      }</td></tr>
1346:             * <tr><td>{@link SwingConstants#SOUTH_WEST}</td><td>{@link SwingConstants#SOUTH}</td><td>{@link SwingConstants#SOUTH_EAST}</td></tr>
1347:             * </table>
1348:             *
1349:             * These constants designate the edge visible on screen.  For example,
1350:             * {@code NORTH} always designates the top edge on the screen.
1351:             * However, this could correspond to another edge of the logical
1352:             * shape {@code this} depending on the affine transform which was 
1353:             * specified during the last call to {@link #setTransform}. For example,
1354:             * {@code AffineTransform.getScaleInstance(+1,-1)} has the effect of
1355:             * inverting the y axis so that the <var>y</var><sub>max</sub> values
1356:             * appear to the North rather than the <var>y</var><sub>min</sub> values.
1357:             *
1358:             * @param toRepaint Component to repaint after a field has been edited,
1359:             *                  or {@code null} if there isn't one.
1360:             *
1361:             * @return       An editor in which the user can specify the position of
1362:             *               one of the edges of the geometric shape.
1363:             * @throws       IllegalArgumentException if {@code side} isn't one
1364:             *               of the recognised codes.
1365:             */
1366:            public synchronized JComponent addEditor(final Format format,
1367:                    final int side, Component toRepaint)
1368:                    throws IllegalArgumentException {
1369:                final JComponent component;
1370:                final JFormattedTextField editor;
1371:                if (format instanceof  DecimalFormat) {
1372:                    final SpinnerNumberModel model = new SpinnerNumberModel();
1373:                    final JSpinner spinner = new JSpinner(model);
1374:                    final JSpinner.NumberEditor sedt = (JSpinner.NumberEditor) spinner
1375:                            .getEditor();
1376:                    final DecimalFormat targetFormat = sedt.getFormat();
1377:                    final DecimalFormat sourceFormat = (DecimalFormat) format;
1378:                    // TODO: Next lines would be much more efficient if only we had a
1379:                    // NumberEditor.setFormat(NumberFormat) method (See RFE #4520587)
1380:                    targetFormat.setDecimalFormatSymbols(sourceFormat
1381:                            .getDecimalFormatSymbols());
1382:                    targetFormat.applyPattern(sourceFormat.toPattern());
1383:                    editor = sedt.getTextField();
1384:                    component = spinner;
1385:                } else if (format instanceof  SimpleDateFormat) {
1386:                    final SpinnerDateModel model = new SpinnerDateModel();
1387:                    final JSpinner spinner = new JSpinner(model);
1388:                    final JSpinner.DateEditor sedt = (JSpinner.DateEditor) spinner
1389:                            .getEditor();
1390:                    final SimpleDateFormat targetFormat = sedt.getFormat();
1391:                    final SimpleDateFormat sourceFormat = (SimpleDateFormat) format;
1392:                    // TODO: Next lines would be much more efficient if only we had a
1393:                    // DateEditor.setFormat(DateFormat) method... (See RFE #4520587)
1394:                    targetFormat.setDateFormatSymbols(sourceFormat
1395:                            .getDateFormatSymbols());
1396:                    targetFormat.applyPattern(sourceFormat.toPattern());
1397:                    editor = sedt.getTextField();
1398:                    component = spinner;
1399:                } else {
1400:                    component = editor = new JFormattedTextField(format);
1401:                }
1402:                /**
1403:                 * "9" is the default width of text fields.  These widths are expressed
1404:                 * in number of columns.  <i>Swing</i> does not appear to measure these
1405:                 * widths very accurately; it seems to provide more than requested.
1406:                 * For that reason, we specify a narrower width.
1407:                 */
1408:                editor.setColumns(5);
1409:                editor.setHorizontalAlignment(JTextField.RIGHT);
1410:                Insets insets = editor.getMargin();
1411:                insets.right += 2;
1412:                editor.setMargin(insets);
1413:                /*
1414:                 * Adds the editor to the list of editors to control.  Increasing the
1415:                 * 'editors' array length each time is not a very efficient strategy,
1416:                 * but it will do because it is unlikely that we will ever add more
1417:                 * than 4 editors.
1418:                 */
1419:                final Control control = new Control(editor,
1420:                        (format instanceof  DateFormat),
1421:                        convertSwingConstant(side), toRepaint);
1422:                if (editors == null) {
1423:                    editors = new Control[1];
1424:                } else {
1425:                    editors = (Control[]) XArray.resize(editors,
1426:                            editors.length + 1);
1427:                }
1428:                editors[editors.length - 1] = control;
1429:                return component;
1430:            }
1431:
1432:            /**
1433:             * Removes an editor from the list of those which display the
1434:             * coordinates of the visor.
1435:             *
1436:             * @param editor Editor to remove.
1437:             */
1438:            public synchronized void removeEditor(final JComponent editor) {
1439:                if (editors != null) {
1440:                    for (int i = 0; i < editors.length; i++) {
1441:                        if (editors[i].editor == editor) {
1442:                            editors = (Control[]) XArray.remove(editors, i, 1);
1443:                            /*
1444:                             * In principal, there should be no more objects to 
1445:                             * remove from the table.  But we let the loop continue
1446:                             * anyway, just in case...
1447:                             */
1448:                        }
1449:                    }
1450:                    if (editors.length == 0) {
1451:                        editors = null;
1452:                    }
1453:                }
1454:            }
1455:
1456:            /**
1457:             * When the position of one of the rectangle's edges is edited manually,
1458:             * specifies whether the opposite edge should also be adjusted. By default,
1459:             * the edges are not synchronised.
1460:             *
1461:             * @param axis {@link SwingConstants#HORIZONTAL} to change the
1462:             *             synchronisation of the left and right edges, or
1463:             *             {@link SwingConstants#VERTICAL} to change the 
1464:             *             synchronisation of the top and bottom edges.
1465:             * @param state {@code true} to synchronise the edges, or
1466:             *              {@code false} to desynchronise.
1467:             * @throws IllegalArgumentException if {@code axis}
1468:             *         isn't one of the valid codes.
1469:             */
1470:            public void setEditorsSynchronized(final int axis,
1471:                    final boolean state) throws IllegalArgumentException {
1472:                switch (axis) {
1473:                case SwingConstants.HORIZONTAL:
1474:                    synchronizeX = state;
1475:                    break;
1476:                case SwingConstants.VERTICAL:
1477:                    synchronizeY = state;
1478:                    break;
1479:                default:
1480:                    throw new IllegalArgumentException();
1481:                }
1482:            }
1483:
1484:            /**
1485:             * When the position of one of the rectangle's edges is edited manually,
1486:             * specifies whether the opposite edge should also be adjusted. By default,
1487:             * the edges are not synchronised.
1488:             *
1489:             * @param axis {@link SwingConstants#HORIZONTAL} to determine the
1490:             *             synchronisation of the left and right edges, or
1491:             *             {@link SwingConstants#VERTICAL} to determine the
1492:             *             synchronisation of the top and bottom edges.
1493:             * @return {@code true} if the specified edges are synchronised,
1494:             *         or {@code false} if not
1495:             * @throws IllegalArgumentException if {@code axis}
1496:             *         isn't one of the valid codes.
1497:             */
1498:            public boolean isEditorsSynchronized(final int axis)
1499:                    throws IllegalArgumentException {
1500:                switch (axis) {
1501:                case SwingConstants.HORIZONTAL:
1502:                    return synchronizeX;
1503:                case SwingConstants.VERTICAL:
1504:                    return synchronizeY;
1505:                default:
1506:                    throw new IllegalArgumentException();
1507:                }
1508:            }
1509:
1510:            /**
1511:             * Returns a character string representing this object.
1512:             */
1513:            public String toString() {
1514:                return Utilities.getShortClassName(this ) + '['
1515:                        + Utilities.getShortClassName(logicalShape) + ']';
1516:            }
1517:
1518:            /**
1519:             * Synchronises one of the rectangle's edges with a text field.  Each time
1520:             * the visor moves, the text will be updated.  If, on the contrary, it is 
1521:             * the text which is manually edited, the visor will be repositioned.
1522:             *
1523:             * @version 1.0
1524:             * @author Martin Desruisseaux
1525:             */
1526:            private final class Control implements  PropertyChangeListener {
1527:                /**
1528:                 * Text field representing the coordinate of one of the visor's
1529:                 * edges.
1530:                 */
1531:                public final JFormattedTextField editor;
1532:
1533:                /**
1534:                 * {@code true} if the field {@link #editor} formats dates,
1535:                 * or {@code false} if it formats numbers.
1536:                 */
1537:                private final boolean isDate;
1538:
1539:                /**
1540:                 * Side of the rectangle to be controlled. This field designates the
1541:                 * edge which is visible on screen.  For example, {@code NORTH}
1542:                 * always designates the top edge on the screen.  However, this could
1543:                 * correspond to another edge of the logical shape 
1544:                 * {@link MouseReshapeTracker} depending on the affine transform that
1545:                 * was specified during the last call to 
1546:                 * {@link MouseReshapeTracker#setTransform}. For example,
1547:                 * {@code AffineTransform.getScaleInstance(+1,-1)} has the effect
1548:                 * of inverting the y axis so that the <var>y</var><sub>max</sub>
1549:                 * values appear to the North rather than the
1550:                 * <var>y</var><sub>min</sub> values.
1551:                 */
1552:                private final int side;
1553:
1554:                /**
1555:                 * Component to repaint after the field is edited, or {@code null}
1556:                 * if there isn't one.
1557:                 */
1558:                private final Component toRepaint;
1559:
1560:                /**
1561:                 * Constructs an object which will control one of the rectangle's edges.
1562:                 *
1563:                 * @param editor Field which will contain the coordinate of the
1564:                 *        rectangle's edge.
1565:                 * @param isDate {@code true} if the field {@link #editor} formats
1566:                 *        dates, or {@code false} if it formats numbers.
1567:                 * @param side Edge of the rectangle to control.  This argument
1568:                 *        designates the edge visible on screen.  For example,
1569:                 *        {@code NORTH} always designates the top edge on the
1570:                 *        screen.  However, it can correspond to another edge of the
1571:                 *        logical shape {@link MouseReshapeTracker} depending on the 
1572:                 *        affine transform which was specified during the last call
1573:                 *        to {@link MouseReshapeTracker#setTransform}. For example,
1574:                 *        {@code AffineTransform.getScaleInstance(+1,-1)} has the
1575:                 *        effect of making the <var>y</var><sub>max</sub> values 
1576:                 *        appear to the "North" rather than the
1577:                 *        <var>y</var><sub>min</sub> values.
1578:                 * @param toRepaint Component to repaint after the field has been
1579:                 *        edited, or {@code null} if there isn't one.
1580:                 */
1581:                public Control(final JFormattedTextField editor,
1582:                        final boolean isDate, final int side,
1583:                        final Component toRepaint) {
1584:                    this .editor = editor;
1585:                    this .isDate = isDate;
1586:                    this .side = side;
1587:                    this .toRepaint = toRepaint;
1588:                    updateText(editor);
1589:                    editor.addPropertyChangeListener("value", this );
1590:                }
1591:
1592:                /**
1593:                 * Method called automatically each time the value in the editor
1594:                 * changes.
1595:                 */
1596:                public void propertyChange(final PropertyChangeEvent event) {
1597:                    final Object source = event.getSource();
1598:                    if (source instanceof  JFormattedTextField) {
1599:                        final JFormattedTextField editor = (JFormattedTextField) source;
1600:                        final Object value = editor.getValue();
1601:                        if (value != null) {
1602:                            final double v = (value instanceof  Date) ? ((Date) value)
1603:                                    .getTime()
1604:                                    : ((Number) value).doubleValue();
1605:                            if (!Double.isNaN(v)) {
1606:                                /*
1607:                                 * Obtains the new coordinates of the rectangle, 
1608:                                 * taking into account the coordinates changed by the
1609:                                 * user as well as the old coordinates which have not
1610:                                 * changed.
1611:                                 */
1612:                                final int side = inverseTransform(this .side);
1613:                                double Vxmin = (side & WEST) == 0 ? logicalShape
1614:                                        .getMinX()
1615:                                        : v;
1616:                                double Vxmax = (side & EAST) == 0 ? logicalShape
1617:                                        .getMaxX()
1618:                                        : v;
1619:                                double Vymin = (side & NORTH) == 0 ? logicalShape
1620:                                        .getMinY()
1621:                                        : v;
1622:                                double Vymax = (side & SOUTH) == 0 ? logicalShape
1623:                                        .getMaxY()
1624:                                        : v;
1625:                                if (synchronizeX || Vxmin > Vxmax) {
1626:                                    final double dx = logicalShape.getWidth();
1627:                                    if ((side & WEST) != 0)
1628:                                        Vxmax = Vxmin + dx;
1629:                                    if ((side & EAST) != 0)
1630:                                        Vxmin = Vxmax - dx;
1631:                                }
1632:                                if (synchronizeY || Vymin > Vymax) {
1633:                                    final double dy = logicalShape.getHeight();
1634:                                    if ((side & NORTH) != 0)
1635:                                        Vymax = Vymin + dy;
1636:                                    if ((side & SOUTH) != 0)
1637:                                        Vymin = Vymax - dy;
1638:                                }
1639:                                /*
1640:                                 * Checks whether the new coordinates need a clip
1641:                                 * adjustment.  If so, we ask the method
1642:                                 * 'clipChangeRequested' to make the change.  This
1643:                                 * 'clipChangeRequested' method doesn't have to accept
1644:                                 * the change.  The rest of the code will be correct
1645:                                 * even if the clip hasn't changed (in that case the
1646:                                 * position of the rectangle will still be adjusted
1647:                                 * by 'setFrame').
1648:                                 */
1649:                                if (Vxmin < xmin) {
1650:                                    final double dx = Math.max(xmax - xmin,
1651:                                            MINSIZE_RATIO * (Vxmax - Vxmin));
1652:                                    final double margin = Vxmax + dx
1653:                                            * ((MINSIZE_RATIO - 1) * 0.5);
1654:                                    clipChangeRequested(margin - dx, margin,
1655:                                            ymin, ymax);
1656:                                } else if (Vxmax > xmax) {
1657:                                    final double dx = Math.max(xmax - xmin,
1658:                                            MINSIZE_RATIO * (Vxmax - Vxmin));
1659:                                    final double margin = Vxmin - dx
1660:                                            * ((MINSIZE_RATIO - 1) * 0.5);
1661:                                    clipChangeRequested(margin, margin + dx,
1662:                                            ymin, ymax);
1663:                                }
1664:                                if (Vymin < ymin) {
1665:                                    final double dy = Math.max(ymax - ymin,
1666:                                            MINSIZE_RATIO * (Vymax - Vymin));
1667:                                    final double margin = Vymax + dy
1668:                                            * ((MINSIZE_RATIO - 1) * 0.5);
1669:                                    clipChangeRequested(xmin, xmax,
1670:                                            margin - dy, margin);
1671:                                } else if (Vymax > ymax) {
1672:                                    final double dy = Math.max(ymax - ymin,
1673:                                            MINSIZE_RATIO * (Vymax - Vymin));
1674:                                    final double margin = Vymin - dy
1675:                                            * ((MINSIZE_RATIO - 1) * 0.5);
1676:                                    clipChangeRequested(xmin, xmax, margin,
1677:                                            margin + dy);
1678:                                }
1679:                                /*
1680:                                 * Repositions the rectangle based on the new
1681:                                 * coordinates.
1682:                                 */
1683:                                if (setFrame(Vxmin, Vymin, Vxmax - Vxmin, Vymax
1684:                                        - Vymin)) {
1685:                                    if (toRepaint != null)
1686:                                        toRepaint.repaint();
1687:                                }
1688:                            }
1689:                        }
1690:                        updateText(editor);
1691:                    }
1692:                }
1693:
1694:                /**
1695:                 * Called each time the position of the rectangle is adjusted.  This
1696:                 * method will adjust the value displayed in the text field
1697:                 * based on the position of the rectangle.
1698:                 */
1699:                private void updateText(final JFormattedTextField editor) {
1700:                    String text;
1701:                    if (!logicalShape.isEmpty()
1702:                            || ((text = editor.getText()) != null && text
1703:                                    .trim().length() != 0)) {
1704:                        double value;
1705:                        switch (inverseTransform(side)) {
1706:                        case NORTH:
1707:                            value = logicalShape.getMinY();
1708:                            break;
1709:                        case SOUTH:
1710:                            value = logicalShape.getMaxY();
1711:                            break;
1712:                        case WEST:
1713:                            value = logicalShape.getMinX();
1714:                            break;
1715:                        case EAST:
1716:                            value = logicalShape.getMaxX();
1717:                            break;
1718:                        default:
1719:                            return;
1720:                        }
1721:                        editor.setValue(isDate ? (Object) new Date(Math
1722:                                .round(value)) : (Object) new Double(value));
1723:                    }
1724:                }
1725:
1726:                /**
1727:                 * Updates the text which appears in {@link #editor}
1728:                 * based on the current position of the rectangle.
1729:                 */
1730:                public void updateText() {
1731:                    updateText(editor);
1732:                }
1733:            }
1734:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.