PolygonScaleHandle.java

/*
 * @(#)PolygonScaleHandle.java
 *
 * Project:		JHotdraw - a GUI framework for technical drawings
 *				http://www.jhotdraw.org
 *				http://jhotdraw.sourceforge.net
 * Copyright:	© by the original author(s) and all contributors
 * License:		Lesser GNU Public License (LGPL)
 *				http://www.opensource.org/licenses/lgpl-license.html
 */

package CH.ifa.draw.contrib;

import CH.ifa.draw.framework.*;
import CH.ifa.draw.standard.*;
import CH.ifa.draw.util.Geom;
import CH.ifa.draw.util.Undoable;
import CH.ifa.draw.util.UndoableAdapter;
import java.awt.*;

/**
 * A Handle to scale and rotate a PolygonFigure
 * Based on RadiusHandle
 *
 * @author Doug Lea  (dl at gee, Sat Mar 1 09:06:09 1997)
 * @version <$CURRENT_VERSION$>
 */
class PolygonScaleHandle extends AbstractHandle {
	
	private Point fCurrent;
	
	public PolygonScaleHandle(PolygonFigure owner) {
		super(owner);
	}
	
	/**
	 * @param x the x position where the interaction started
	 * @param y the y position where the interaction started
	 * @param view the handles container
	 */
	public void invokeStart(int x, int  y, DrawingView view) {
		fCurrent = new Point(x, y);
		PolygonScaleHandle.UndoActivity activity = (PolygonScaleHandle.UndoActivity)createUndoActivity(view);
		setUndoActivity(activity);
		activity.setAffectedFigures(new SingleFigureEnumerator(owner()));
		activity.setPolygon(((PolygonFigure)(owner())).getPolygon());
	}

	/**
	 * Tracks a step of the interaction.
	 * @param x the current x position
	 * @param y the current y position
	 * @param anchorX the x position where the interaction started
	 * @param anchorY the y position where the interaction started
	 */
	public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
		fCurrent = new Point(x, y);
		Polygon polygon = ((PolygonScaleHandle.UndoActivity)getUndoActivity()).getPolygon();
		((PolygonFigure)(owner())).scaleRotate(new Point(anchorX, anchorY), polygon, fCurrent);
	}

	/**
	 * Tracks the end of the interaction.
	 * @param x the current x position
	 * @param y the current y position
	 * @param anchorX the x position where the interaction started
	 * @param anchorY the y position where the interaction started
	 */
	public void invokeEnd(int x, int y, int anchorX, int anchorY, DrawingView view) {
		((PolygonFigure)(owner())).smoothPoints();
		if ((fCurrent.x == anchorX) && (fCurrent.y == anchorY)) {
			// there is nothing to undo
			setUndoActivity(null);
		}
		fCurrent = null;
	}
	
	public Point locate() {
		if (fCurrent == null) {
			return getOrigin();
		}
		else {
			return fCurrent;
		}
	}
	
	Point getOrigin() {
		// find a nice place to put handle
		// Need to pick a place that will not overlap with point handle
		// and is internal to polygon
	
		// Try for one HANDLESIZE step away from outermost toward center
	
		Point outer = ((PolygonFigure)(owner())).outermostPoint();
		Point ctr = ((PolygonFigure)(owner())).center();
		double len = Geom.length(outer.x, outer.y, ctr.x, ctr.y);
		if (len == 0) { // best we can do?
			return new Point(outer.x - HANDLESIZE/2, outer.y + HANDLESIZE/2);
		}
	
		double u = HANDLESIZE / len;
		if (u > 1.0) { // best we can do?
			return new Point((outer.x * 3 + ctr.x)/4, (outer.y * 3 + ctr.y)/4);
		}
		else {
			return new Point((int)(outer.x * (1.0 - u) + ctr.x * u),
			(int)(outer.y * (1.0 - u) + ctr.y * u));
		}
	}
	
	public void draw(Graphics g) {
		Rectangle r = displayBox();
	
		g.setColor(Color.yellow);
		g.fillOval(r.x, r.y, r.width, r.height);
	
		g.setColor(Color.black);
		g.drawOval(r.x, r.y, r.width, r.height);
	
		/* for debugging ...
		Point ctr = ((PolygonFigure)(owner())).center();
		g.setColor(Color.blue);
		g.fillOval(ctr.x, ctr.y, r.width, r.height);
		
		g.setColor(Color.black);
		g.drawOval(ctr.x, ctr.y, r.width, r.height);
		
		*/
	}

	/**
	 * Factory method for undo activity. To be overriden by subclasses.
	 */
	protected Undoable createUndoActivity(DrawingView newView) {
		return new PolygonScaleHandle.UndoActivity(newView);
	}
	
	public static class UndoActivity extends UndoableAdapter {
		private Polygon myPolygon;
		
		public UndoActivity(DrawingView newView) {
			super(newView);
			setUndoable(true);
			setRedoable(true);
		}
		
		public boolean undo() {
			if (!super.undo()) {
				return false;
			}

			return resetPolygon();
		}
	
		public boolean redo() {
			// do not call execute directly as the selection might has changed
			if (!isRedoable()) {
				return false;
			}

			return resetPolygon();
		}

		protected boolean resetPolygon() {
			FigureEnumeration fe = getAffectedFigures();
			if (!fe.hasMoreElements()) {
				return false;
			}
			PolygonFigure figure = (PolygonFigure)fe.nextFigure();
			Polygon backupPolygon = figure.getPolygon();
			figure.willChange();
			figure.setInternalPolygon(getPolygon());
			figure.changed();
			setPolygon(backupPolygon);
			return true;
		}
		
		protected void setPolygon(Polygon newPolygon) {
			myPolygon = newPolygon;
		}
		
		public Polygon getPolygon() {
			return myPolygon;
		}
	}
}