]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ResizeNode.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / ResizeNode.java
index 79c10c1cd3b435e992d7b98f742a9258bcecf38a..b18be83b3ee30d9d4cd298212659bb257abf47d5 100644 (file)
-package org.simantics.diagram.elements;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Cursor;\r
-import java.awt.Graphics2D;\r
-import java.awt.Shape;\r
-import java.awt.Stroke;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-\r
-import org.simantics.g2d.canvas.impl.CanvasContext;\r
-import org.simantics.scenegraph.g2d.G2DNode;\r
-import org.simantics.scenegraph.g2d.events.EventTypes;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
-import org.simantics.scenegraph.utils.NodeUtil;\r
-\r
-/**\r
- * Invisible resize node for resizing rectangular elements\r
- * \r
- * @author Teemu Lempinen\r
- *\r
- */\r
-public class ResizeNode extends G2DNode {\r
-       \r
-       private static final long serialVersionUID = 7997312998598071328L;\r
-\r
-       \r
-       /**\r
-        * Interface for listeners listening resize events in nodes\r
-        * @author Teemu Lempinen\r
-        *\r
-        */\r
-       public interface ResizeListener {\r
-           \r
-           /**\r
-            * Triggered when a node has been resized\r
-            * @param newBounds new bounds for the node\r
-            */\r
-           public void elementResized(Rectangle2D newBounds, AffineTransform transform, boolean synchronizeToBackend);\r
-       }\r
-       \r
-       /**\r
-        * Enumeration for indicating which side of the resize bounds should affect translate\r
-        * properties. \r
-        * @author Teemu Lempinen\r
-        *\r
-        */\r
-       public enum TranslateEdge {\r
-           NONE, NORTH, SOUTH, EAST, WEST;\r
-       }\r
-       \r
-       \r
-       private boolean dragging = false;\r
-       private ResizeListener resizeListener;\r
-       private Rectangle2D bounds;\r
-       private Stroke stroke;\r
-       private TranslateEdge xTranslateEdge = TranslateEdge.WEST;\r
-       private TranslateEdge YTranslateEdge = TranslateEdge.NORTH;\r
-\r
-       int cursor = Cursor.DEFAULT_CURSOR;\r
-       \r
-       /**\r
-        * Create a new Resize node with default border width (1)\r
-        */\r
-       public ResizeNode() {\r
-               this(1);\r
-       }\r
-        \r
-       /**\r
-        * Create a new Resize node\r
-        * \r
-        * @param borderWidth Width of the border for handling mouse dragging\r
-        */\r
-       public ResizeNode(float borderWidth) {\r
-               this.stroke = new BasicStroke(borderWidth);\r
-       }\r
-\r
-       @PropertySetter("Bounds")\r
-       @SyncField("bounds")\r
-       public void setBounds(Rectangle2D bounds) {\r
-               assert(bounds != null);\r
-               this.bounds = bounds;\r
-       }\r
-\r
-       @PropertySetter("stroke")\r
-       @SyncField("stroke")\r
-       public void setStroke(Stroke stroke) {\r
-               this.stroke = stroke;\r
-       }\r
-\r
-       /**\r
-        * Is dragging (resizing) active\r
-        * @return\r
-        */\r
-       public boolean dragging() {\r
-               return dragging;\r
-       }\r
-\r
-       /**\r
-        * Set a ResizeListener for this node\r
-        * @param listener ResizeListener\r
-        */\r
-       public void setResizeListener(ResizeListener listener) {\r
-               this.resizeListener = listener;\r
-       }\r
-\r
-       @Override\r
-       public void cleanup() {\r
-               removeEventHandler(this);\r
-               super.cleanup();\r
-       }\r
-\r
-       @Override\r
-       public void init() {\r
-               super.init();\r
-               addEventHandler(this);\r
-       }\r
-\r
-       /**\r
-        * Outline stroke shape of the bounds of this node\r
-        * \r
-        * @return Outline stroke shape of the bounds of this node\r
-        */\r
-       protected Shape getOutline() {\r
-               return stroke.createStrokedShape(new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()));\r
-       }\r
-\r
-       /**\r
-        * Dragging is started on mouse pressed, if mouse was pressed on the edge of bounds\r
-        */\r
-       @Override\r
-       protected boolean mouseButtonPressed(MouseButtonPressedEvent event) {\r
-               if(bounds != null && NodeUtil.isSelected(this, 1)) {\r
-                       // get mouse position\r
-                       Point2D local = controlToLocal( event.controlPosition );\r
-                       local = parentToLocal(local);\r
-\r
-                       // Get outline of this node\r
-                       Shape outline = getOutline();\r
-\r
-                       if (outline.contains(local)) {\r
-                               dragging = true;\r
-                               return true;\r
-                       }\r
-               }\r
-               return super.mouseButtonPressed(event);\r
-       }\r
-\r
-       /**\r
-        * Get resize cursor for the location where the drag started.\r
-        * \r
-        * @param local Point of origin for drag\r
-        * @return Cursor int\r
-        */\r
-       private int getCursorDirection(Point2D local) {\r
-               float width = ((BasicStroke)stroke).getLineWidth();\r
-\r
-               // Check the direction of the resize\r
-               int cursor = 0;\r
-               if (local.getX() >= bounds.getX() - width / 2 && \r
-                               local.getX() <= bounds.getX() + width / 2)\r
-                       // West side\r
-                       cursor = Cursor.W_RESIZE_CURSOR;\r
-               else if  (local.getX() >= bounds.getMaxX() - width / 2 && \r
-                               local.getX() <= bounds.getMaxX() + width / 2)\r
-                       // East size\r
-                       cursor = Cursor.E_RESIZE_CURSOR;\r
-\r
-               if(local.getY() >= bounds.getY() - width / 2 && \r
-                               local.getY() <= bounds.getY() + width / 2) {\r
-                       // North side\r
-                       if(cursor == Cursor.W_RESIZE_CURSOR)\r
-                               cursor = Cursor.NW_RESIZE_CURSOR;\r
-                       else if(cursor == Cursor.E_RESIZE_CURSOR)\r
-                               cursor = Cursor.NE_RESIZE_CURSOR;\r
-                       else\r
-                               cursor = Cursor.N_RESIZE_CURSOR;\r
-               } else if(local.getY() >= bounds.getMaxY() - width / 2 && \r
-                               local.getY() <= bounds.getMaxY() + width / 2) {\r
-                       // South side\r
-                       if(cursor == Cursor.W_RESIZE_CURSOR)\r
-                               cursor = Cursor.SW_RESIZE_CURSOR;\r
-                       else if(cursor == Cursor.E_RESIZE_CURSOR)\r
-                               cursor = Cursor.SE_RESIZE_CURSOR;\r
-                       else\r
-                               cursor = cursor | Cursor.S_RESIZE_CURSOR;\r
-               }\r
-               return cursor;\r
-       }\r
-\r
-       double dragTolerance = 0.5;\r
-       \r
-       @Override\r
-       protected boolean mouseMoved(MouseMovedEvent e) {\r
-               if(dragging) {\r
-                       // If dragging is active and mouse is moved enough, resize the element\r
-                       Point2D local = controlToLocal( e.controlPosition );\r
-                       local = parentToLocal(local);\r
-\r
-                       Rectangle2D bounds = getBoundsInLocal().getBounds2D();\r
-\r
-                       if(Math.abs(bounds.getMaxX() - local.getX()) > dragTolerance ||\r
-                                       Math.abs(bounds.getMaxY() - local.getY()) > dragTolerance) {\r
-                               resize(local, false);\r
-                       }\r
-                       return true;\r
-               } else if(NodeUtil.isSelected(this, 1)){\r
-                       // Dragging is not active. Change mouse cursor if entered or exited border\r
-                       Point2D local = controlToLocal( e.controlPosition );\r
-                       local = parentToLocal(local);\r
-\r
-                       Shape outline = getOutline();\r
-                       if (outline.contains(local)) {\r
-                               cursor = getCursorDirection(local);\r
-                               CanvasContext ctx = (CanvasContext)e.getContext();\r
-                               ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor));\r
-                       } else if(cursor != 0) {\r
-                               cursor = 0;\r
-                               CanvasContext ctx = (CanvasContext)e.getContext();\r
-                               ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));\r
-                       }\r
-\r
-               }\r
-               return super.mouseMoved(e);\r
-       }\r
-\r
-       @Override\r
-       protected boolean mouseDragged(MouseDragBegin e) {\r
-               if(dragging) {\r
-                       return true; // Consume event\r
-               } else {\r
-                       return false;\r
-               }\r
-       }\r
-       \r
-       private static double minSize = 5; // Minimum size for an element\r
-\r
-       private void resize(Point2D local, boolean synchronize) {\r
-               Rectangle2D bounds = getBoundsInLocal().getBounds2D();\r
-               double x = bounds.getX();\r
-               double y = bounds.getY();\r
-               double w = bounds.getWidth();\r
-               double h = bounds.getHeight();\r
-               double dx = 0, dy = 0;\r
-\r
-               /*\r
-                * Resize to east and north:\r
-                * \r
-                * Move x and y coordinates and resize to get maxX and maxY to stay in their place\r
-                */\r
-               if(cursor == Cursor.W_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR) {\r
-                       double dw = local.getX() - x;\r
-                       if(w - dw < minSize)\r
-                               dw = w - minSize;\r
-                       w = w - dw;\r
-                       \r
-                       \r
-                       if(TranslateEdge.WEST.equals(xTranslateEdge))\r
-                           dx = dw;\r
-               } \r
-               \r
-               if(cursor == Cursor.N_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR ) {\r
-                       double dh = local.getY() - y;\r
-                       if(h - dh < minSize)\r
-                               dh = h - minSize;\r
-                       h = h - dh;\r
-                       \r
-                       if(TranslateEdge.NORTH.equals(YTranslateEdge))\r
-                           dy = dh;\r
-               } \r
-               \r
-               /*\r
-                * Resize to west and south:\r
-                * \r
-                * Adjust width and height\r
-                */\r
-               if(cursor == Cursor.E_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) {\r
-                       double dw = local.getX() - bounds.getMaxX();\r
-                       w = w + dw > minSize ? w + dw : minSize;\r
-                       \r
-                       if(TranslateEdge.EAST.equals(xTranslateEdge))\r
-                           dx = w - bounds.getWidth();\r
-               }\r
-\r
-               if(cursor == Cursor.S_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) {\r
-                   double dh = local.getY() - bounds.getMaxY();\r
-                   h = h + dh > minSize ? h + dh : minSize;\r
-\r
-                   if(TranslateEdge.SOUTH.equals(YTranslateEdge))\r
-                       dy = h - bounds.getHeight();\r
-               }\r
-\r
-               /*\r
-                *  Set bounds and transform to the element before calling resize listener.\r
-                *  This prevents unwanted movement due to unsynchronized transform and bounds.\r
-                */\r
-               this.bounds.setRect(x, y, w, h);\r
-\r
-        AffineTransform at = new AffineTransform();\r
-               at.translate(dx, dy);\r
-               if(resizeListener != null)\r
-                       resizeListener.elementResized(this.bounds, at, synchronize);\r
-       }\r
-\r
-       @Override\r
-       protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {\r
-               if(dragging) {\r
-                       // Stop resizing and set the size to its final state. \r
-                       Point2D local = controlToLocal( e.controlPosition );\r
-                       local = parentToLocal(local);\r
-\r
-                       resize(local, true);\r
-                       dragging = false;\r
-\r
-                       // Revert cursor to normal.\r
-                       CanvasContext ctx = (CanvasContext)e.getContext();\r
-                       cursor = Cursor.DEFAULT_CURSOR;\r
-                       ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor));\r
-               }\r
-               return super.mouseButtonReleased(e);\r
-       }\r
-\r
-       @Override\r
-       public int getEventMask() {\r
-               return super.getEventMask() | EventTypes.MouseButtonPressedMask\r
-                               | EventTypes.MouseMovedMask\r
-                               | EventTypes.MouseButtonReleasedMask\r
-                               ;\r
-       }\r
-\r
-       @Override\r
-       public Rectangle2D getBoundsInLocal() {\r
-               if(bounds == null) return null;\r
-               return bounds.getBounds2D();\r
-       }\r
-\r
-       @Override\r
-       public void render(Graphics2D g2d) {\r
-               // Do not draw anything\r
-       }\r
-       \r
-       /**\r
-        * Set which edge should affect X-translation \r
-        * @param xTranslateEdge TranslateEdge.NONE, EAST or WEST\r
-        */\r
-       public void setxTranslateEdge(TranslateEdge xTranslateEdge) {\r
-        this.xTranslateEdge = xTranslateEdge;\r
-    }\r
-       \r
-          /**\r
-     * Set which edge should affect Y-translation \r
-     * @param yTranslateEdge TranslateEdge.NONE, SOUTH or NORTH\r
-     */\r
-       public void setYTranslateEdge(TranslateEdge yTranslateEdge) {\r
-        YTranslateEdge = yTranslateEdge;\r
-    }\r
-}\r
+package org.simantics.diagram.elements;
+
+import java.awt.BasicStroke;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import org.simantics.g2d.canvas.impl.CanvasContext;
+import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.g2d.events.EventTypes;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
+import org.simantics.scenegraph.utils.NodeUtil;
+
+/**
+ * Invisible resize node for resizing rectangular elements
+ * 
+ * @author Teemu Lempinen
+ *
+ */
+public class ResizeNode extends G2DNode {
+       
+       private static final long serialVersionUID = 7997312998598071328L;
+
+       
+       /**
+        * Interface for listeners listening resize events in nodes
+        * @author Teemu Lempinen
+        *
+        */
+       public interface ResizeListener {
+           
+           /**
+            * Triggered when a node has been resized
+            * @param newBounds new bounds for the node
+            */
+           public void elementResized(Rectangle2D newBounds, AffineTransform transform, boolean synchronizeToBackend);
+       }
+       
+       /**
+        * Enumeration for indicating which side of the resize bounds should affect translate
+        * properties. 
+        * @author Teemu Lempinen
+        *
+        */
+       public enum TranslateEdge {
+           NONE, NORTH, SOUTH, EAST, WEST;
+       }
+       
+       
+       private boolean dragging = false;
+       private ResizeListener resizeListener;
+       private Rectangle2D bounds;
+       private Stroke stroke;
+       private TranslateEdge xTranslateEdge = TranslateEdge.WEST;
+       private TranslateEdge YTranslateEdge = TranslateEdge.NORTH;
+
+       int cursor = Cursor.DEFAULT_CURSOR;
+       
+       /**
+        * Create a new Resize node with default border width (1)
+        */
+       public ResizeNode() {
+               this(1);
+       }
+        
+       /**
+        * Create a new Resize node
+        * 
+        * @param borderWidth Width of the border for handling mouse dragging
+        */
+       public ResizeNode(float borderWidth) {
+               this.stroke = new BasicStroke(borderWidth);
+       }
+
+       @PropertySetter("Bounds")
+       @SyncField("bounds")
+       public void setBounds(Rectangle2D bounds) {
+               assert(bounds != null);
+               this.bounds = bounds;
+       }
+
+       @PropertySetter("stroke")
+       @SyncField("stroke")
+       public void setStroke(Stroke stroke) {
+               this.stroke = stroke;
+       }
+
+       /**
+        * Is dragging (resizing) active
+        * @return
+        */
+       public boolean dragging() {
+               return dragging;
+       }
+
+       /**
+        * Set a ResizeListener for this node
+        * @param listener ResizeListener
+        */
+       public void setResizeListener(ResizeListener listener) {
+               this.resizeListener = listener;
+       }
+
+       @Override
+       public void cleanup() {
+               removeEventHandler(this);
+               super.cleanup();
+       }
+
+       @Override
+       public void init() {
+               super.init();
+               addEventHandler(this);
+       }
+
+       /**
+        * Outline stroke shape of the bounds of this node
+        * 
+        * @return Outline stroke shape of the bounds of this node
+        */
+       protected Shape getOutline() {
+               return stroke.createStrokedShape(new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()));
+       }
+
+       /**
+        * Dragging is started on mouse pressed, if mouse was pressed on the edge of bounds
+        */
+       @Override
+       protected boolean mouseButtonPressed(MouseButtonPressedEvent event) {
+               if(bounds != null && NodeUtil.isSelected(this, 1)) {
+                       // get mouse position
+                       Point2D local = controlToLocal( event.controlPosition );
+                       local = parentToLocal(local);
+
+                       // Get outline of this node
+                       Shape outline = getOutline();
+
+                       if (outline.contains(local)) {
+                               dragging = true;
+                               return true;
+                       }
+               }
+               return super.mouseButtonPressed(event);
+       }
+
+       /**
+        * Get resize cursor for the location where the drag started.
+        * 
+        * @param local Point of origin for drag
+        * @return Cursor int
+        */
+       private int getCursorDirection(Point2D local) {
+               float width = ((BasicStroke)stroke).getLineWidth();
+
+               // Check the direction of the resize
+               int cursor = 0;
+               if (local.getX() >= bounds.getX() - width / 2 && 
+                               local.getX() <= bounds.getX() + width / 2)
+                       // West side
+                       cursor = Cursor.W_RESIZE_CURSOR;
+               else if  (local.getX() >= bounds.getMaxX() - width / 2 && 
+                               local.getX() <= bounds.getMaxX() + width / 2)
+                       // East size
+                       cursor = Cursor.E_RESIZE_CURSOR;
+
+               if(local.getY() >= bounds.getY() - width / 2 && 
+                               local.getY() <= bounds.getY() + width / 2) {
+                       // North side
+                       if(cursor == Cursor.W_RESIZE_CURSOR)
+                               cursor = Cursor.NW_RESIZE_CURSOR;
+                       else if(cursor == Cursor.E_RESIZE_CURSOR)
+                               cursor = Cursor.NE_RESIZE_CURSOR;
+                       else
+                               cursor = Cursor.N_RESIZE_CURSOR;
+               } else if(local.getY() >= bounds.getMaxY() - width / 2 && 
+                               local.getY() <= bounds.getMaxY() + width / 2) {
+                       // South side
+                       if(cursor == Cursor.W_RESIZE_CURSOR)
+                               cursor = Cursor.SW_RESIZE_CURSOR;
+                       else if(cursor == Cursor.E_RESIZE_CURSOR)
+                               cursor = Cursor.SE_RESIZE_CURSOR;
+                       else
+                               cursor = cursor | Cursor.S_RESIZE_CURSOR;
+               }
+               return cursor;
+       }
+
+       double dragTolerance = 0.5;
+       
+       @Override
+       protected boolean mouseMoved(MouseMovedEvent e) {
+               if(dragging) {
+                       // If dragging is active and mouse is moved enough, resize the element
+                       Point2D local = controlToLocal( e.controlPosition );
+                       local = parentToLocal(local);
+
+                       Rectangle2D bounds = getBoundsInLocal().getBounds2D();
+
+                       if(Math.abs(bounds.getMaxX() - local.getX()) > dragTolerance ||
+                                       Math.abs(bounds.getMaxY() - local.getY()) > dragTolerance) {
+                               resize(local, false);
+                       }
+                       return true;
+               } else if(NodeUtil.isSelected(this, 1)){
+                       // Dragging is not active. Change mouse cursor if entered or exited border
+                       Point2D local = controlToLocal( e.controlPosition );
+                       local = parentToLocal(local);
+
+                       Shape outline = getOutline();
+                       if (outline.contains(local)) {
+                               cursor = getCursorDirection(local);
+                               CanvasContext ctx = (CanvasContext)e.getContext();
+                               ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor));
+                       } else if(cursor != 0) {
+                               cursor = 0;
+                               CanvasContext ctx = (CanvasContext)e.getContext();
+                               ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+                       }
+
+               }
+               return super.mouseMoved(e);
+       }
+
+       @Override
+       protected boolean mouseDragged(MouseDragBegin e) {
+               if(dragging) {
+                       return true; // Consume event
+               } else {
+                       return false;
+               }
+       }
+       
+       private static double minSize = 5; // Minimum size for an element
+
+       private void resize(Point2D local, boolean synchronize) {
+               Rectangle2D bounds = getBoundsInLocal().getBounds2D();
+               double x = bounds.getX();
+               double y = bounds.getY();
+               double w = bounds.getWidth();
+               double h = bounds.getHeight();
+               double dx = 0, dy = 0;
+
+               /*
+                * Resize to east and north:
+                * 
+                * Move x and y coordinates and resize to get maxX and maxY to stay in their place
+                */
+               if(cursor == Cursor.W_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR) {
+                       double dw = local.getX() - x;
+                       if(w - dw < minSize)
+                               dw = w - minSize;
+                       w = w - dw;
+                       
+                       
+                       if(TranslateEdge.WEST.equals(xTranslateEdge))
+                           dx = dw;
+               } 
+               
+               if(cursor == Cursor.N_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR ) {
+                       double dh = local.getY() - y;
+                       if(h - dh < minSize)
+                               dh = h - minSize;
+                       h = h - dh;
+                       
+                       if(TranslateEdge.NORTH.equals(YTranslateEdge))
+                           dy = dh;
+               } 
+               
+               /*
+                * Resize to west and south:
+                * 
+                * Adjust width and height
+                */
+               if(cursor == Cursor.E_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) {
+                       double dw = local.getX() - bounds.getMaxX();
+                       w = w + dw > minSize ? w + dw : minSize;
+                       
+                       if(TranslateEdge.EAST.equals(xTranslateEdge))
+                           dx = w - bounds.getWidth();
+               }
+
+               if(cursor == Cursor.S_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) {
+                   double dh = local.getY() - bounds.getMaxY();
+                   h = h + dh > minSize ? h + dh : minSize;
+
+                   if(TranslateEdge.SOUTH.equals(YTranslateEdge))
+                       dy = h - bounds.getHeight();
+               }
+
+               /*
+                *  Set bounds and transform to the element before calling resize listener.
+                *  This prevents unwanted movement due to unsynchronized transform and bounds.
+                */
+               this.bounds.setRect(x, y, w, h);
+
+        AffineTransform at = new AffineTransform();
+               at.translate(dx, dy);
+               if(resizeListener != null)
+                       resizeListener.elementResized(this.bounds, at, synchronize);
+       }
+
+       @Override
+       protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
+               if(dragging) {
+                       // Stop resizing and set the size to its final state. 
+                       Point2D local = controlToLocal( e.controlPosition );
+                       local = parentToLocal(local);
+
+                       resize(local, true);
+                       dragging = false;
+
+                       // Revert cursor to normal.
+                       CanvasContext ctx = (CanvasContext)e.getContext();
+                       cursor = Cursor.DEFAULT_CURSOR;
+                       ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor));
+               }
+               return super.mouseButtonReleased(e);
+       }
+
+       @Override
+       public int getEventMask() {
+               return super.getEventMask() | EventTypes.MouseButtonPressedMask
+                               | EventTypes.MouseMovedMask
+                               | EventTypes.MouseButtonReleasedMask
+                               ;
+       }
+
+       @Override
+       public Rectangle2D getBoundsInLocal() {
+               if(bounds == null) return null;
+               return bounds.getBounds2D();
+       }
+
+       @Override
+       public void render(Graphics2D g2d) {
+               // Do not draw anything
+       }
+       
+       /**
+        * Set which edge should affect X-translation 
+        * @param xTranslateEdge TranslateEdge.NONE, EAST or WEST
+        */
+       public void setxTranslateEdge(TranslateEdge xTranslateEdge) {
+        this.xTranslateEdge = xTranslateEdge;
+    }
+       
+          /**
+     * Set which edge should affect Y-translation 
+     * @param yTranslateEdge TranslateEdge.NONE, SOUTH or NORTH
+     */
+       public void setYTranslateEdge(TranslateEdge yTranslateEdge) {
+        YTranslateEdge = yTranslateEdge;
+    }
+}