/******************************************************************************* * <copyright> * * Copyright (c) 2011, 2012 SAP AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API, implementation and documentation * mwenz - Bug 358255 - Add Border/Background decorators * * </copyright> * *******************************************************************************/ package org.eclipse.graphiti.examples.chess.features; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.graphiti.examples.chess.Messages; import org.eclipse.graphiti.examples.chess.MoveUtil; import org.eclipse.graphiti.examples.chess.diagram.ChessToolBehaviorProvider; import org.eclipse.graphiti.examples.mm.chess.Piece; import org.eclipse.graphiti.examples.mm.chess.Square; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.ICreateConnectionContext; import org.eclipse.graphiti.features.context.impl.AddConnectionContext; import org.eclipse.graphiti.features.impl.AbstractCreateConnectionFeature; import org.eclipse.graphiti.mm.pictograms.Anchor; import org.eclipse.graphiti.mm.pictograms.AnchorContainer; import org.eclipse.graphiti.mm.pictograms.BoxRelativeAnchor; import org.eclipse.graphiti.mm.pictograms.Connection; import org.eclipse.graphiti.mm.pictograms.ContainerShape; import org.eclipse.graphiti.mm.pictograms.PictogramElement; public class CreateChessMoveFeature extends AbstractCreateConnectionFeature { public CreateChessMoveFeature(IFeatureProvider fp) { super(fp, Messages.CreateChessMoveFeature_name, Messages.CreateChessMoveFeature_description); } public boolean canStartConnection(ICreateConnectionContext context) { // We can start a move connection at every anchor that belong to a // square holding a piece or that belongs to a move connection Piece piece = getPiece(context.getSourceAnchor()); return piece != null; } public boolean canCreate(ICreateConnectionContext context) { // Get the piece to move (potentially follow connection) Piece piece = getPiece(context.getSourceAnchor()); if (piece == null) { return false; } // The start square for the current move Square sourceSquare = getSquare(context.getSourceAnchor()); if (sourceSquare == null) { return false; } // The end square for the current move Square targetSquare = getSquare(context.getTargetAnchor()); if (targetSquare == null) { return false; } // Check if the piece can move from start to end square return MoveUtil.isMoveAllowed(piece, sourceSquare, targetSquare); } public Connection create(ICreateConnectionContext context) { // Take back the highlighting takeBackHighlighting(); Connection newConnection = null; Anchor sourceAnchor = context.getSourceAnchor(); // Get Squares to be connected Square sourceSquare = getPiece(sourceAnchor).getSquare(); Square targetSquare = getSquare(context.getTargetAnchor()); if (sourceSquare != null && targetSquare != null) { AddConnectionContext addContext; AnchorContainer parent = sourceAnchor.getParent(); if (parent instanceof ContainerShape) { // Add connection for normal container shape (either piece or // square) addContext = new AddConnectionContext(getSquareConnectionAnchor(sourceAnchor), getSquareConnectionAnchor(context.getTargetAnchor())); } else if (parent instanceof Connection) { // Add connection for move connection, use end anchor addContext = new AddConnectionContext(((Connection) parent).getEnd(), getSquareConnectionAnchor(context.getTargetAnchor())); } else { throw new IllegalStateException("Parent in neither a ContainerShape nor a Connection: " + parent); //$NON-NLS-1$ } // Set the property identifying a move connection addContext.putProperty(MoveUtil.PROPERTY_MOVE, Boolean.TRUE); // Add the connection to the diagram newConnection = (Connection) getFeatureProvider().addIfPossible(addContext); } return newConnection; } @Override public void attachedToSource(ICreateConnectionContext context) { // Called as soon as a connection is started Piece piece = getPiece(context.getSourceAnchor()); if (piece == null) { return; } Square sourceSquare = getSquare(context.getSourceAnchor()); if (sourceSquare == null) { return; } // Highlight all allowed squares. showFeedback(context); } @Override public void canceledAttaching(ICreateConnectionContext context) { // Take back the highlighting takeBackHighlighting(); } private void takeBackHighlighting() { ChessToolBehaviorProvider toolBehaviorProvider = (ChessToolBehaviorProvider) getFeatureProvider() .getDiagramTypeProvider().getCurrentToolBehaviorProvider(); toolBehaviorProvider.clearAllowedSquaresForMove(); getDiagramBehavior().refresh(); } private void showFeedback(ICreateConnectionContext context) { // Find the piece that shall be moved (potentially follow move // connections) Piece piece = getPiece(context.getSourceAnchor()); if (piece == null) { return; } // Find the square to start th move step from Square sourceSquare = getSquare(context.getSourceAnchor()); if (sourceSquare == null) { return; } // Find all allowed squares to move to EList<Square> allSquares = sourceSquare.getBoard().getSquares(); final List<Square> allowedSquares = new ArrayList<Square>(); for (Square square : allSquares) { if (MoveUtil.isMoveAllowed(piece, sourceSquare, square)) { allowedSquares.add(square); } } // Mark the allowed squares using decorators ChessToolBehaviorProvider toolBehaviorProvider = (ChessToolBehaviorProvider) getFeatureProvider() .getDiagramTypeProvider().getCurrentToolBehaviorProvider(); toolBehaviorProvider.addToAllowedSquaresForMove(allowedSquares); for (Square square : allowedSquares) { PictogramElement pe = getFeatureProvider().getPictogramElementForBusinessObject(square); getDiagramBehavior().refreshRenderingDecorators(pe); } } private Piece getPiece(Anchor anchor) { // Try to find a piece for the given anchor if (anchor != null) { AnchorContainer parent = anchor.getParent(); Object obj = getBusinessObjectForPictogramElement(parent); if (obj instanceof Piece) { // The shape of the anchor represents the piece return (Piece) obj; } else if (obj instanceof Square) { // The shape of the anchor represents a square Piece pieceOnSquare = ((Square) obj).getPiece(); if (pieceOnSquare != null) { // Return the piece on the square return pieceOnSquare; } // No piece on the square, check for move connection EList<Connection> incomingConnections = anchor.getIncomingConnections(); for (Connection connection : incomingConnections) { // Follow the first connection back to a piece Piece piece = getPiece(connection.getStart()); if (piece != null) { return piece; } } } else if (parent instanceof Connection) { // Anchor of a connection, follow it backwards to its piece Anchor startAnchor = ((Connection) parent).getStart(); return getPiece(startAnchor); } } return null; } private Square getSquare(Anchor anchor) { // Try to find a square for the given anchor if (anchor != null) { AnchorContainer parent = anchor.getParent(); Object obj = getBusinessObjectForPictogramElement(parent); if (obj instanceof Square) { // The shape of the anchor represents a square return (Square) obj; } else if (obj instanceof Piece) { // The shape of the anchor represents a piece return ((Piece) obj).getSquare(); } else if (parent instanceof Connection) { // The anchor belongs to a connection; get the square at its end Anchor startAnchor = ((Connection) parent).getEnd(); return getSquare(startAnchor); } } return null; } private Anchor getSquareConnectionAnchor(Anchor anchor) { // Find the anchor to attach a move connection to a square if (anchor != null) { AnchorContainer parent = anchor.getParent(); if (parent instanceof ContainerShape) { Object obj = getBusinessObjectForPictogramElement(parent); if (obj instanceof Square) { // Anchor belongs to a shape that represents a square EList<Anchor> anchors = parent.getAnchors(); return findConnectionAnchor(anchors); } else if (obj instanceof Piece) { // Anchor belongs to a shape that represents a piece, use // the anchors of the parent shape EList<Anchor> anchors = ((ContainerShape) parent).getContainer().getAnchors(); return findConnectionAnchor(anchors); } } else { throw new IllegalStateException("Parent shape is not a container shape"); //$NON-NLS-1$ } } return null; } private Anchor findConnectionAnchor(EList<Anchor> anchors) { // Return the right anchor from the list of found anchors (the // BoxRelativeAnchor is used for connections while the ChopboxAnchor // only catches the connection attachment) for (Anchor connectionAnchor : anchors) { if (connectionAnchor instanceof BoxRelativeAnchor) { return connectionAnchor; } } throw new IllegalStateException("No BoxRelativeAnchor found"); //$NON-NLS-1$ } }