Java tutorial
/** * Squidy Interaction Library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * Squidy Interaction Library is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Squidy Interaction Library. If not, see * <http://www.gnu.org/licenses/>. * * 2009 Human-Computer Interaction Group, University of Konstanz. * <http://hci.uni-konstanz.de> * * Please contact info@squidy-lib.de or visit our website * <http://www.squidy-lib.de> for further information. */ package org.squidy.designer.shape; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.awt.geom.RoundRectangle2D; import javax.xml.bind.Unmarshaller; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squidy.Storable; import org.squidy.database.BaseXSessionProvider; import org.squidy.designer.Designer; import org.squidy.designer.DesignerPreferences; import org.squidy.designer.Initializable; import org.squidy.designer.constant.Constants; import org.squidy.designer.dragndrop.Draggable; import org.squidy.designer.event.MultiSelectionHandler; import org.squidy.designer.knowledgebase.AdvancedKnowledgeBase; import org.squidy.designer.model.IModelStore; import org.squidy.designer.model.PipeShape; import org.squidy.designer.model.WorkspaceShape; import org.squidy.designer.shape.modularity.NodeBased; import org.squidy.designer.util.ShapeUtils; import org.squidy.designer.zoom.ZoomState; import org.squidy.designer.zoom.Zoomable; import edu.umd.cs.piccolo.PCamera; import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.activities.PActivity; import edu.umd.cs.piccolo.activities.PActivity.PActivityDelegate; import edu.umd.cs.piccolo.activities.PTransformActivity; import edu.umd.cs.piccolo.event.PBasicInputEventHandler; import edu.umd.cs.piccolo.event.PDragEventHandler; import edu.umd.cs.piccolo.event.PInputEvent; import edu.umd.cs.piccolo.util.PBounds; import edu.umd.cs.piccolo.util.PDimension; import edu.umd.cs.piccolo.util.PPaintContext; /** * <code>ZoomShape</code>. * * <pre> * Date: Jan 31, 2009 * Time: 6:55:05 PM * </pre> * * @author <pre> * Roman R&aumldle * <a href="mailto:Roman.Raedle@uni-konstanz.de">Roman.Raedle@uni-konstanz.de</a> * Human-Computer Interaction Group * University of Konstanz * </pre> * * @version $Id: ZoomShape.java 772 2011-09-16 15:39:44Z raedle $ * @since 1.0.0 */ public abstract class ZoomShape<T extends VisualShape<?>> extends VisualShape<T> implements Initializable, Zoomable { /** * Generated serial version UID. */ private static final long serialVersionUID = -2712173601846983666L; // Logger to info, error, debug,... messages. private static Log LOG = LogFactory.getLog(ZoomShape.class); public static final String ZOOM_BEGAN = "ZOOM_BEGAN"; public static final String ZOOM_ENDED = "ZOOM_ENDED"; // ############################################################################# // BEGIN JAXB // ############################################################################# /** * */ public ZoomShape() { this(true); } /* * (non-Javadoc) * * @see * org.squidy.designer.VisualShape#afterUnmarshal(javax.xml.bind. * Unmarshaller, java.lang.Object) */ public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { super.afterUnmarshal(unmarshaller, parent); } // ############################################################################# // END JAXB // ############################################################################# // ############################################################################# // BEGIN Initializable // ############################################################################# /* * (non-Javadoc) * * @see org.squidy.designer.Initializable#initialize() */ public void initialize() { super.initialize(); for (VisualShape<?> child : getChildren()) { if (child instanceof Initializable) { ((Initializable) child).initialize(); } } } // ############################################################################# // END Initializable // ############################################################################# // ############################################################################# // BEGIN INTERNAL // ############################################################################# /** * Reflects the current zoom state of the zoom shape. * * @see this{@link #layoutSemantics(ZoomState)} * @see this{@link #layoutSemanticsZoomedIn()} * @see this{@link #layoutSemanticsZoomedOut()} */ protected ZoomState currentZoomState; private boolean zoomInProgress = false; /** * @return * * TODO [RR]: Please review because of performance aspects!!!! */ public boolean isZoomInProgress() { return zoomInProgress; } /** * Returns whether a zoom activity is in progress. If no zoom activity is in * progress for this node a hierarchical search will be performed for parent * node and its activity. * * @return Whether a zoom activity is in progress or not. * * TODO [RR]: Please review because of performance aspects!!!! */ public boolean isHierarchicalZoomInProgress() { if (!zoomInProgress) { boolean parentZoomInProgress = false; PNode parent = getParent(); while (parent != null && !parentZoomInProgress) { if (parent instanceof ZoomShape<?>) { parentZoomInProgress = ((ZoomShape<?>) parent).isZoomInProgress(); if (parentZoomInProgress) { return parentZoomInProgress; } } parent = parent.getParent(); } } boolean childZoomInProgress = false; for (Object o : getChildrenReference()) { if (o instanceof ZoomShape) { childZoomInProgress = ((ZoomShape<?>) o).isHierarchicalZoomInProgress(); if (childZoomInProgress) { return childZoomInProgress; } } } return zoomInProgress; } private Shape multiSelection; private boolean allowMultiSelection = true; /** * @return the allowMultiSelection */ public final boolean isAllowMultiSelection() { return allowMultiSelection; } /** * @param allowMultiSelection * the allowMultiSelection to set */ public final void setAllowMultiSelection(boolean allowMultiSelection) { this.allowMultiSelection = allowMultiSelection; } private PNode knowledgeBase; /** * @param goalDirectedZoom */ public ZoomShape(boolean goalDirectedZoom) { setBounds(Constants.DEFAULT_NODE_BOUNDS); if (goalDirectedZoom) { addInputEventListener(new PBasicInputEventHandler() { @Override public void mouseClicked(PInputEvent event) { if (!event.isHandled() && event.isLeftMouseButton() && event.getClickCount() == 2) { PNode store = event.getPickedNode(); while (store != null && !(store instanceof IModelStore)) { store = store.getParent(); } PNode node = event.getPickedNode(); while (node != null && !(node instanceof ZoomShape<?>)) { node = node.getParent(); } if (node instanceof ZoomShape<?>) { // if (LOG.isDebugEnabled()) { // LOG.debug("Selected node to zoom is " + node + " and this is " + ZoomShape.this); // } node.moveToFront(); // PBounds boundsView = event.getCamera() // .getViewBounds(); // PBounds boundsNode = ((ZoomShape<?>) node) // .getGlobalBoundsZoomedIn(); // boolean sameNode = (Math.abs(boundsView.x // - boundsNode.x) < 0.1 && Math // .abs(boundsView.width - boundsNode.width) < 0.1) // || (Math.abs(boundsView.y - boundsNode.y) < 0.1 && Math // .abs(boundsView.height // - boundsNode.height) < 0.1); // // if (!sameNode) { if (store != null) { ((IModelStore) store).getModel().setZoomedShape((ZoomShape<?>) node); ((IModelStore) store).getModel().setZoomedBounds(getGlobalBoundsZoomedIn()); } animateToCenterView(event.getCamera()); // } event.setHandled(true); } } } }); } if (this instanceof Draggable) { addInputEventListener(new PDragEventHandler() { private PNode draggable; /* * (non-Javadoc) * * @seeedu.umd.cs.piccolo.event.PDragEventHandler# * shouldStartDragInteraction * (edu.umd.cs.piccolo.event.PInputEvent) */ @Override protected boolean shouldStartDragInteraction(PInputEvent event) { if (!event.isHandled()) { PNode node = event.getPickedNode(); while (node != null && !(node instanceof Draggable)) { node = node.getParent(); } // Set dragged node to allow drag transformation. draggable = node; if (node instanceof Draggable) { if (((Draggable) node).isDraggable() && super.shouldStartDragInteraction(event)) { return true; } } return false; } return false; } /* * (non-Javadoc) * * @see * edu.umd.cs.piccolo.event.PDragEventHandler#drag(edu.umd.cs * .piccolo .event.PInputEvent) */ @Override protected void drag(PInputEvent event) { if (!event.isHandled()) { if (!event.getPath().acceptsNode(draggable)) { if (LOG.isDebugEnabled()) { LOG.debug("Pick path doesn't accept node " + draggable.getClass().getName() + "."); } return; } PNode parent = draggable.getParent(); PDimension d = event.getDeltaRelativeTo(draggable); draggable.localToParent(d); PBounds parentBounds = parent.getBoundsReference(); Rectangle2D draggableBounds = draggable .localToParent(draggable.getBounds().moveBy(d.getWidth(), d.getHeight())); if (parentBounds.contains(draggableBounds)) { draggable.offset(d.getWidth(), d.getHeight()); } Point2D offset = draggable.getOffset(); if (offset.getX() < 0) { draggable.setOffset(0, offset.getY()); } if (offset.getY() < 0) { draggable.setOffset(offset.getX(), 0); } if (offset.getX() > parentBounds.getWidth() - draggableBounds.getWidth()) { draggable.setOffset(parentBounds.getWidth() - draggableBounds.getWidth(), offset.getY()); } if (offset.getY() > parentBounds.getHeight() - draggableBounds.getHeight()) { draggable.setOffset(offset.getX(), parentBounds.getHeight() - draggableBounds.getHeight()); } event.setHandled(true); } // if (!event.isHandled()) { // if (!event.getPath().acceptsNode(draggable)) { // if (LOG.isDebugEnabled()) { // LOG.debug("Pick path doesn't accept node " + // draggable.getClass().getName() + "."); // } // return; // } // // Point2D current = // event.getPositionRelativeTo(ZoomShape.this); // draggable.localToParent(current); // // Point2D dest = new Point2D.Double(); // // dest.setLocation((current.getX()), (current.getY())); // // dest.setLocation(dest.getX() - (dest.getX() % 20), // dest.getY() - (dest.getY() % 20)); // // // dest.setLocation(nodeStartPosition.getX() - (d.getX() // % 20), nodeStartPosition.getY() - (d.getY() % 20)); // // System.out.println("OFFSET: " + dest); // // draggable.setOffset(dest.getX(), dest.getY()); // // // } // event.setHandled(true); // } } }); } MultiSelectionHandler multiSelectionHandler = new MultiSelectionHandler() { /* (non-Javadoc) * @see org.squidy.designer.event.MultiSelectionHandler#selectionAllowed(edu.umd.cs.piccolo.event.PInputEvent) */ @Override protected boolean selectionAllowed(PInputEvent event) { PNode node = event.getPickedNode(); return node instanceof Draggable && !((Draggable) node).isDraggable(); } /* * (non-Javadoc) * * @see * org.squidy.designer.event.MultiSelectionHandler#startSelection * (edu.umd.cs.piccolo.event.PInputEvent, java.awt.Shape) */ @Override public void startSelection(PInputEvent event, Shape selectionShape) { if (!event.isHandled()) { multiSelection = selectionShape; event.setHandled(true); invalidatePaint(); } } /* * (non-Javadoc) * * @see * org.squidy.designer.event.MultiSelectionHandler#selection * (edu.umd.cs.piccolo.event.PInputEvent, java.awt.Shape) */ @Override public void selection(PInputEvent event, Shape selectionShape) { if (!event.isHandled()) { multiSelection = selectionShape; event.setHandled(true); invalidatePaint(); } } /* * (non-Javadoc) * * @see * org.squidy.designer.event.MultiSelectionHandler#endSelection * (edu.umd.cs.piccolo.event.PInputEvent, java.awt.Shape) */ @Override public void endSelection(PInputEvent event, Shape selectionShape) { if (!event.isHandled()) { multiSelection = null; for (Object o : getChildrenReference()) { if (o instanceof VisualShape<?>) { VisualShape<?> shape = (VisualShape<?>) o; PBounds bounds = shape.getGlobalFullBounds(); if (selectionShape.contains(bounds)) { System.out.println("containing: " + shape); } } } event.setHandled(true); invalidatePaint(); } } }; // addInputEventListener(multiSelectionHandler); // Add knowledge base to zoom object if class is of type // <code>KnowledgeBased</code>. if (this instanceof NodeBased<?>) { boolean isWorkspace = this instanceof WorkspaceShape; knowledgeBase = new AdvancedKnowledgeBase<ZoomShape<VisualShape<?>>>(isWorkspace); addChild(knowledgeBase); knowledgeBase.setOffset(0, 895); } } /** * @return */ private PBounds getGlobalBoundsZoomedIn() { double border = 50.0 * getGlobalScale(); PBounds bounds = getGlobalBounds(); bounds.add(bounds.getMinX() - border, bounds.getMinY() - border); bounds.add(bounds.getMaxX() + border, bounds.getMaxY() + border); return bounds; } private RoundRectangle2D shapeZoomedOut = new RoundRectangle2D.Double(0., 0., 0., 0., 150., 150.); /** * @return */ protected Shape getZoomedOutShape() { shapeZoomedOut.setFrame(getBoundsReference().getFrame()); return shapeZoomedOut; } private GradientPaint paintZoomedOutFill = new GradientPaint(0f, 0f, Color.WHITE, (float) Constants.DEFAULT_NODE_BOUNDS.getWidth(), (float) Constants.DEFAULT_NODE_BOUNDS.getHeight(), Color.LIGHT_GRAY); /** * @return */ protected Paint getZoomedOutFillPaint() { return paintZoomedOutFill; } /** * @return */ protected Paint getZoomedOutDrawPaint() { return DesignerPreferences.STATUS_STABLE; } private RoundRectangle2D shapeZoomedIn = new RoundRectangle2D.Double(0., 0., 0., 0., 25., 25.); /** * @return */ protected Shape getZoomedInShape() { shapeZoomedIn.setFrame(getBoundsReference().getFrame()); return shapeZoomedIn; } /** * @return */ protected Paint getZoomedInFillPaint() { return Color.WHITE; } /** * @return */ protected Paint getZoomedInDrawPaint() { return DesignerPreferences.STATUS_STABLE; } /** * */ protected void zoomBegan() { Storable storable = ShapeUtils.getObjectInHierarchy(Storable.class, this); if (storable != null) { storable.store(); } firePropertyChange(0, ZOOM_BEGAN, false, true); } /** * */ protected void zoomEnded() { if (getParent() != null) { // Set pipe shapes invisible and not pickable. for (Object child : getParent().getChildrenReference()) { if (child instanceof PipeShape) { // ShapeUtils.setApparent((PipeShape) child, false); // ((PipeShape) child).invalidateLayout(); // ((PipeShape) child).invalidateFullBounds(); // ((PipeShape) child).invalidatePaint(); } } } firePropertyChange(0, ZOOM_ENDED, false, true); } private PActivityDelegate delegateAnimateToCenterView = new PActivityDelegate() { /* * (non-Javadoc) * * @seeedu.umd.cs.piccolo.activities.PActivity.PActivityDelegate# * activityStarted(edu.umd.cs.piccolo.activities.PActivity) */ public void activityStarted(PActivity activity) { zoomInProgress = true; zoomBegan(); } /* * (non-Javadoc) * * @seeedu.umd.cs.piccolo.activities.PActivity.PActivityDelegate# * activityFinished(edu.umd.cs.piccolo.activities.PActivity) */ public void activityFinished(PActivity activity) { zoomInProgress = false; zoomEnded(); } /* * (non-Javadoc) * * @seeedu.umd.cs.piccolo.activities.PActivity.PActivityDelegate# * activityStepped(edu.umd.cs.piccolo.activities.PActivity) */ public void activityStepped(PActivity activity) { zoomInProgress = true; } }; /** * Animates the current shape to the maximum bounds of the camera. * * @param camera * The camera that gets the current shape as maximum bounds to be * viewed. */ public void animateToCenterView(PCamera camera) { // Set current node as NOT draggable. if (this instanceof Draggable) { ((Draggable) this).setDraggable(false); } // Set all children of current node as draggable. for (Object child : getChildrenReference()) { if (child instanceof Draggable) { ((Draggable) child).setDraggable(true); } } // Start zoom and mark current shape as zoom in progress. PTransformActivity activity = camera.animateViewToCenterBounds(getGlobalBoundsZoomedIn(), true, 500); activity.setDelegate(delegateAnimateToCenterView); } private static final Color COLOR_MULTI_SELECTION = new Color(0, 0, 255, 20); private static final Stroke STROKE_MULTI_SELECTION = new BasicStroke(0.1f); private static final Stroke STROKE_SEMANTIC_ZOOM = new BasicStroke(10f); private static final Stroke STROKE_ZOOM_OUT = new BasicStroke(25f); private static final Stroke STROKE_ZOOMED_OUT = new BasicStroke(5f); // SketchyStroke(40f, // 25f, // 5f) private static final Stroke STROKE_ZOOMED_IN = new BasicStroke(5f); // SketchyStroke(8f, // 5f, // 1f) @Override protected void paintAfterChildren(PPaintContext paintContext) { super.paintAfterChildren(paintContext); Graphics2D g = paintContext.getGraphics(); if (multiSelection != null) { g.setColor(COLOR_MULTI_SELECTION); g.fill(globalToLocal((Rectangle2D) ((RectangularShape) multiSelection).clone())); g.setColor(Color.BLUE); // System.out.println(getGlobalScale()); g.setStroke(STROKE_MULTI_SELECTION); g.draw(globalToLocal((Rectangle2D) ((RectangularShape) multiSelection).clone())); } } /* * (non-Javadoc) * * @see * org.squidy.designer.VisualShape#paintShape(edu.umd.cs.piccolo. * util.PPaintContext) */ @Override protected void paintShape(PPaintContext paintContext) { super.paintShape(paintContext); Graphics2D g = paintContext.getGraphics(); double scale = paintContext.getScale(); g.setColor(Color.BLACK); if (scale > Constants.SEMANTIC_ZOOM_SCALE) { g.setStroke(STROKE_SEMANTIC_ZOOM); layoutSemantics(ZoomState.ZOOM_IN); paintShapeZoomedIn(paintContext); } else { g.setStroke(STROKE_ZOOM_OUT); layoutSemantics(ZoomState.ZOOM_OUT); paintShapeZoomedOut(paintContext); } } /** * @param paintContext */ protected void paintShapeZoomedOut(PPaintContext paintContext) { Shape shapeZoomedOut = getZoomedOutShape(); if (shapeZoomedOut != null) { Graphics2D g = paintContext.getGraphics(); Rectangle bounds = shapeZoomedOut.getBounds(); g.setPaint(getZoomedOutFillPaint()); if (isRenderPrimitiveRect()) g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); else g.fill(shapeZoomedOut); g.setStroke(STROKE_ZOOMED_OUT); g.setPaint(getZoomedOutDrawPaint()); if (isRenderPrimitiveRect()) g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height); else g.draw(shapeZoomedOut); } } /** * @param paintContext */ protected void paintShapeZoomedIn(PPaintContext paintContext) { Shape shapeZoomedIn = getZoomedInShape(); if (shapeZoomedIn != null) { Graphics2D g = paintContext.getGraphics(); Rectangle bounds = shapeZoomedIn.getBounds(); g.setPaint(getZoomedInFillPaint()); if (isRenderPrimitiveRect()) g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); else g.fill(shapeZoomedIn); g.setStroke(STROKE_ZOOMED_IN); g.setPaint(getZoomedInDrawPaint()); if (isRenderPrimitiveRect()) g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height); else g.draw(shapeZoomedIn); } } /** * Layout the shape for a current zoom state. This method delegates either * to this{@link #layoutSemanticsZoomedIn()} or this * {@link #layoutSemanticsZoomedOut()} method. * * @param zoomState * The current progressed zoom state. * * @see this{@link #currentZoomState} * @see this{@link #layoutSemanticsZoomedIn()} * @see this{@link #layoutSemanticsZoomedOut()} */ protected void layoutSemantics(ZoomState zoomState) { // Simple case. Returns if no zoom state change occured. if (currentZoomState == zoomState) { return; } currentZoomState = zoomState; switch (currentZoomState) { case ZOOM_IN: layoutSemanticsZoomedIn(); break; case ZOOM_OUT: layoutSemanticsZoomedOut(); break; } } /** * Allows to layout semantics for current zoomed in shape. */ protected void layoutSemanticsZoomedIn() { // Knowledge base. if (knowledgeBase != null) { ShapeUtils.setApparent(knowledgeBase, true); } } /** * Allows to layout semantics for current zoomed out shape. */ protected void layoutSemanticsZoomedOut() { // Knowledge base. if (knowledgeBase != null) { ShapeUtils.setApparent(knowledgeBase, false); } } // ############################################################################# // END INTERNAL // ############################################################################# }