public void propertyChange(PropertyChangeEvent evt) {
   ShapeCreationPanel scp = (ShapeCreationPanel) evt.getSource();
   if (SCALE_FACTOR_KEY.equals(evt.getPropertyName())) {
     refreshScaleFactor(scp);
     scp.repaint();
   } else if (CONSTRAINT_KEY.equals(evt.getPropertyName())) {
     scp.repaint();
   } else if (ShapeCreationPanel.DATA_MODEL_KEY.equals(evt.getPropertyName())) {
     ((DataModel) evt.getOldValue()).removeListener(myDataModelListener);
     invalidateMirror(scp);
     ((DataModel) evt.getNewValue()).addListener(myDataModelListener);
   } else if (ShapeCreationPanel.MODE_KEY.equals(evt.getPropertyName())) {
     if (ShapeCreationPanel.MODE_CREATE.equals(evt.getNewValue())) {
       scp.getSelectionModel().select(-1, -1, null);
     }
     updateCursor(scp);
   }
 }
        @Override
        public void mousePressed(MouseEvent e) {
          ShapeCreationPanel scp = (ShapeCreationPanel) e.getComponent();
          scp.requestFocus();
          boolean isCreating = ShapeCreationPanel.MODE_CREATE.equals(scp.getMode());
          if (e.getClickCount() > 1 && isCreating) {
            scp.setMode(ShapeCreationPanel.MODE_DEFAULT);
            return;
          }

          lastUntransformedX = e.getX();
          lastUntransformedY = e.getY();
          Point2D p = new Point2D.Double(e.getX(), e.getY());
          try {
            scp.getTransform().createInverse().transform(p, p);
          } catch (NoninvertibleTransformException e2) {
            throw new RuntimeException(e2);
          }
          float x = (float) p.getX();
          float y = (float) p.getY();

          clickX = x;
          clickY = y;

          if (isCreating) {
            int selectedShape = scp.getSelectionModel().getSelection().getShapeIndex();
            if (selectedShape == -1) {
              GeneralPath path = new GeneralPath();
              path.moveTo(x, y);
              int newIndex = scp.getDataModel().addShape(path);
              scp.getSelectionModel().select(newIndex, 0, Handle.NEXT_CONTROL);
            } else {
              /**
               * We didn't give a next control point to the *last* node yet. Now we do. If the last
               * node in a path has a next control point: that path will be closed.
               */
              CubicPath[] paths = getCubicPaths(scp);
              CubicPath path = paths[selectedShape];
              int i = path.getNodeCount() - 1;
              Point2D lastPoint = path.getNode(i, null);
              Point2D lastPointCtrlPoint = path.getPrevControlForNode(i, null);
              if (lastPointCtrlPoint == null) lastPointCtrlPoint = lastPoint;

              double dx = lastPoint.getX() - lastPointCtrlPoint.getX();
              double dy = lastPoint.getY() - lastPointCtrlPoint.getY();
              path.setNextControlForNode(
                  i, new Point2D.Double(lastPoint.getX() + dx, lastPoint.getY() + dy));

              path.lineTo(x, y);

              changingDataModel.add(Thread.currentThread());
              try {
                scp.getDataModel().setShape(selectedShape, path);
              } finally {
                changingDataModel.remove(Thread.currentThread());
              }
            }
          } else {
            Selection selection = getSelection(scp, e.getPoint());
            scp.getSelectionModel().select(selection);
          }
        }
        @Override
        public void mouseDragged(MouseEvent e) {
          try {
            if (e.getClickCount() > 1) return;

            ShapeCreationPanel scp = (ShapeCreationPanel) e.getComponent();
            boolean isCreating = ShapeCreationPanel.MODE_CREATE.equals(scp.getMode());
            CubicPath[] paths = getCubicPaths(scp);
            int i = scp.getSelectionModel().getSelection().getShapeIndex();
            CubicPath path = i == -1 ? null : paths[i];

            Point2D p = new Point2D.Double(e.getX(), e.getY());
            try {
              scp.getTransform().createInverse().transform(p, p);
            } catch (NoninvertibleTransformException e2) {
              throw new RuntimeException(e2);
            }
            float x = (float) p.getX();
            float y = (float) p.getY();
            boolean replacePath = true;

            if (isCreating) {
              int nodeIndex = path.getNodeCount() - 1;
              float dx = x - clickX;
              float dy = y - clickY;
              path.setPrevControlForNode(nodeIndex, clickX - dx, clickY - dy);
            } else {
              Selection s = scp.getSelectionModel().getSelection();
              int nodeIndex = s.getNodeIndex();
              if (nodeIndex != -1) {
                if (Handle.PREVIOUS_CONTROL.equals(s.getHandle())) {
                  path.setPrevControlForNode(nodeIndex, new Point2D.Double(x, y));
                  if (Constraint.ANGLE_ONLY.equals(getConstraint(scp))) {
                    path.setNextControlForNodeFromPrev(nodeIndex, false);
                  } else if (Constraint.ANGLE_AND_DISTANCE.equals(getConstraint(scp))) {
                    path.setNextControlForNodeFromPrev(nodeIndex, true);
                  }
                } else if (Handle.PRIMARY.equals(s.getHandle())) {
                  path.setNode(nodeIndex, new Point2D.Double(x, y), true);
                } else { // next control:
                  path.setNextControlForNode(nodeIndex, new Point2D.Double(x, y));
                  if (Constraint.ANGLE_ONLY.equals(getConstraint(scp))) {
                    path.setPrevControlForNodeFromNext(nodeIndex, false);
                  } else if (Constraint.ANGLE_AND_DISTANCE.equals(getConstraint(scp))) {
                    path.setPrevControlForNodeFromNext(nodeIndex, true);
                  }
                }
              } else {
                float dx = e.getX() - lastUntransformedX;
                float dy = e.getY() - lastUntransformedY;
                nudge(scp, dx, dy);
                replacePath = false;
              }
            }

            if (i >= 0 && replacePath) {
              changingDataModel.add(Thread.currentThread());
              try {
                scp.getDataModel().setShape(i, path);
              } finally {
                changingDataModel.remove(Thread.currentThread());
              }
            }
          } finally {
            lastUntransformedX = e.getX();
            lastUntransformedY = e.getY();
          }
        }