/*
* Copyright (c) 2008 Stiftung Deutsches Elektronen-Synchrotron,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.sds.ui.editparts;
import java.util.List;
import org.csstudio.sds.model.AbstractWidgetModel;
import org.csstudio.sds.model.ContainerModel;
import org.csstudio.sds.model.DisplayModel;
import org.csstudio.sds.model.GuideModel;
import org.csstudio.sds.ui.feedback.IGraphicalFeedbackFactory;
import org.csstudio.sds.ui.internal.commands.AddWidgetCommand;
import org.csstudio.sds.ui.internal.commands.ChangeGuideCommand;
import org.csstudio.sds.ui.internal.commands.CloneCommand;
import org.csstudio.sds.ui.internal.commands.CreateElementCommand;
import org.csstudio.sds.ui.internal.commands.SetBoundsCommand;
import org.csstudio.sds.ui.internal.commands.SetSelectionCommand;
import org.csstudio.sds.ui.internal.feedback.GraphicalFeedbackContributionsService;
import org.csstudio.sds.util.GuideUtil;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.Shape;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.SnapToGuides;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editpolicies.ResizableEditPolicy;
import org.eclipse.gef.editpolicies.XYLayoutEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.rulers.RulerProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The EditPolicy for {@link DisplayModel}. It can be used with
* <code>Figures</code> in {@link XYLayout}. The constraint for XYLayout is a
* {@link org.eclipse.draw2d.geometry.Rectangle}.
*
* @author Sven Wende, Kai Meyer
*/
final class ModelXYLayoutEditPolicy extends XYLayoutEditPolicy {
private static final Logger LOG = LoggerFactory.getLogger(ModelXYLayoutEditPolicy.class);
/**
* Overriden, to provide a generic EditPolicy for children, which is aware
* of different feedback and selection handles. {@inheritDoc}
*/
@Override
protected EditPolicy createChildEditPolicy(final EditPart child) {
return new GenericChildEditPolicy(child);
}
/**
* {@inheritDoc}
*/
@Override
protected Command createAddCommand(final EditPart child,
final Object constraint) {
assert child != null : "child!=null"; //$NON-NLS-1$
assert constraint instanceof Rectangle : "constraint instanceof Rectangle"; //$NON-NLS-1$
ContainerModel container = (ContainerModel) getHost().getModel();
AbstractWidgetModel widget = (AbstractWidgetModel) child.getModel();
CompoundCommand compoundCommand = new CompoundCommand();
compoundCommand.add(new AddWidgetCommand(container, widget));
compoundCommand.add(new SetSelectionCommand(child.getViewer(), widget));
compoundCommand.add(new SetBoundsCommand(widget, (Rectangle) constraint));
return compoundCommand;
}
/**
* {@inheritDoc}
*/
@Override
protected Command createChangeConstraintCommand(
final ChangeBoundsRequest request, final EditPart child,
final Object constraint) {
assert child != null : "child!=null"; //$NON-NLS-1$
assert request != null : "request!=null"; //$NON-NLS-1$
assert constraint != null : "constraint!=null"; //$NON-NLS-1$
assert constraint instanceof Rectangle : "constraint instanceof Rectangle"; //$NON-NLS-1$
AbstractWidgetModel childModel = (AbstractWidgetModel) child.getModel();
Command cmd = null;
IGraphicalFeedbackFactory factory = determineFeedbackFactory(childModel
.getTypeID());
if (factory != null) {
cmd = factory.createChangeBoundsCommand(childModel, request,
(Rectangle) constraint);
// for guide support
if (child instanceof AbstractBaseEditPart) {
AbstractBaseEditPart part = (AbstractBaseEditPart) child;
if ((request.getResizeDirection() & PositionConstants.NORTH_SOUTH) != 0) {
Integer guidePos = (Integer) request.getExtendedData().get(
SnapToGuides.KEY_HORIZONTAL_GUIDE);
if (guidePos != null) {
cmd = chainGuideAttachmentCommand(request, part, cmd,
true);
} else if (GuideUtil.getInstance().getGuide(
part.getWidgetModel(), true) != null) {
// SnapToGuides didn't provide a horizontal guide, but
// this part is attached
// to a horizontal guide. Now we check to see if the
// part is attached to
// the guide along the edge being resized. If that is
// the case, we need to
// detach the part from the guide; otherwise, we leave
// it alone.
int alignment = GuideUtil.getInstance().getGuide(
part.getWidgetModel(), true).getAlignment(
part.getWidgetModel());
int edgeBeingResized = 0;
if ((request.getResizeDirection() & PositionConstants.NORTH) != 0) {
edgeBeingResized = -1;
} else {
edgeBeingResized = 1;
}
if (alignment == edgeBeingResized) {
cmd = cmd.chain(new ChangeGuideCommand(part
.getWidgetModel(), true));
}
}
}
if ((request.getResizeDirection() & PositionConstants.EAST_WEST) != 0) {
Integer guidePos = (Integer) request.getExtendedData().get(
SnapToGuides.KEY_VERTICAL_GUIDE);
if (guidePos != null) {
cmd = chainGuideAttachmentCommand(request, part, cmd,
false);
} else if (GuideUtil.getInstance().getGuide(
part.getWidgetModel(), false) != null) {
int alignment = GuideUtil.getInstance().getGuide(
part.getWidgetModel(), false).getAlignment(
part.getWidgetModel());
int edgeBeingResized = 0;
if ((request.getResizeDirection() & PositionConstants.WEST) != 0) {
edgeBeingResized = -1;
} else {
edgeBeingResized = 1;
}
if (alignment == edgeBeingResized) {
cmd = cmd.chain(new ChangeGuideCommand(part
.getWidgetModel(), false));
}
}
}
if (request.getType().equals(REQ_MOVE_CHILDREN)
|| request.getType().equals(REQ_ALIGN_CHILDREN)) {
cmd = chainGuideAttachmentCommand(request, part, cmd, true);
cmd = chainGuideAttachmentCommand(request, part, cmd, false);
cmd = chainGuideDetachmentCommand(request, part, cmd, true);
cmd = chainGuideDetachmentCommand(request, part, cmd, false);
}
}
}
return cmd;
}
/**
* {@inheritDoc}
*/
@Override
protected Command createChangeConstraintCommand(final EditPart child,
final Object constraint) {
assert false : "This method should never get called."; //$NON-NLS-1$
return null;
}
/**
* {@inheritDoc}
*/
@Override
protected Command getCreateCommand(final CreateRequest request) {
Command result = null;
if (request.getNewObjectType().equals("List_AbstractWidgetModel")) {
CompoundCommand cmd = new CompoundCommand();
// add all widgets
@SuppressWarnings("unchecked")
List<AbstractWidgetModel> widgetList = (List<AbstractWidgetModel>) request
.getNewObject();
Point location = request.getLocation().getCopy();
translateFromAbsoluteToLayoutRelative(location);
for (AbstractWidgetModel abstractWidgetModel : widgetList) {
abstractWidgetModel.setLocation(abstractWidgetModel.getX() + location.x, abstractWidgetModel.getY() + location.y);
}
cmd.add(new AddWidgetCommand((ContainerModel) getHost().getModel(),
widgetList));
// // select all widgets
cmd.add(new
SetSelectionCommand(getHost().getViewer(),
widgetList));
result = cmd;
}
else {
ContainerModel container = (ContainerModel) getHost().getModel();
Rectangle bounds = (Rectangle) getConstraintFor(request);
result = new CreateElementCommand(getHost().getViewer(), container, request, bounds);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected Command getCloneCommand(final ChangeBoundsRequest request) {
CloneCommand clone = new CloneCommand((DisplayModel)getHost().getModel());
GraphicalEditPart currPart = null;
for (Object part : request.getEditParts()) {
currPart = (GraphicalEditPart)part;
clone.addPart((AbstractWidgetModel)currPart.getModel(), (Rectangle)getConstraintForClone(currPart, request));
}
// Attach to horizontal guide, if one is given
Integer guidePos = (Integer)request.getExtendedData()
.get(SnapToGuides.KEY_HORIZONTAL_GUIDE);
if (guidePos != null) {
int hAlignment = ((Integer)request.getExtendedData()
.get(SnapToGuides.KEY_HORIZONTAL_ANCHOR)).intValue();
clone.setGuide(findGuideAt(guidePos.intValue(), true), hAlignment, true);
}
// Attach to vertical guide, if one is given
guidePos = (Integer)request.getExtendedData()
.get(SnapToGuides.KEY_VERTICAL_GUIDE);
if (guidePos != null) {
int vAlignment = ((Integer)request.getExtendedData()
.get(SnapToGuides.KEY_VERTICAL_ANCHOR)).intValue();
clone.setGuide(findGuideAt(guidePos.intValue(), false), vAlignment, false);
}
return clone;
}
/**
* {@inheritDoc}
*/
@Override
public void showTargetFeedback(final Request request) {
if (REQ_ADD.equals(request.getType())
|| REQ_CLONE.equals(request.getType())
|| REQ_MOVE.equals(request.getType())
|| REQ_RESIZE_CHILDREN.equals(request.getType())
|| REQ_CREATE.equals(request.getType())) {
showLayoutTargetFeedback(request);
}
if (REQ_CREATE.equals(request.getType())) {
CreateRequest createReq = (CreateRequest) request;
if (createReq.getSize() != null) {
showSizeOnDropFeedback(createReq);
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void showSizeOnDropFeedback(final CreateRequest request) {
String typeId = determineTypeIdFromRequest(request);
IGraphicalFeedbackFactory feedbackFactory = determineFeedbackFactory(typeId);
assert feedbackFactory != null;
IFigure feedbackFigure = getSizeOnDropFeedback(request);
feedbackFactory.showSizeOnDropFeedback(request, feedbackFigure,
getCreationFeedbackOffset(request));
feedbackFigure.repaint();
}
/**
* Creates a prototype object to determine the type identification of the
* widget model, that is about to be created.
*
* @param request
* the create request
* @return the type identification
*/
@SuppressWarnings("rawtypes")
private String determineTypeIdFromRequest(final CreateRequest request) {
Class newObject = (Class) request.getNewObjectType();
AbstractWidgetModel instance;
String typeId = ""; //$NON-NLS-1$
try {
instance = (AbstractWidgetModel) newObject.newInstance();
typeId = instance.getTypeID();
} catch (InstantiationException e) {
LOG.error(e.toString());
} catch (IllegalAccessException e) {
LOG.error(e.toString());
}
return typeId;
}
/**
* Override to provide custom feedback figure for the given create request.
*
* @param request
* the create request
* @return custom feedback figure
*/
@Override
protected IFigure createSizeOnDropFeedback(final CreateRequest request) {
String typeId = determineTypeIdFromRequest(request);
IGraphicalFeedbackFactory feedbackFactory = determineFeedbackFactory(typeId);
Shape feedbackFigure = feedbackFactory
.createSizeOnDropFeedback(request);
addFeedback(feedbackFigure);
return feedbackFigure;
}
/**
* Gets the determining FeedbackFactory for the given typeID.
*
* @param typeId
* The identifier for the FeedbackFactory
* @return IGraphicalFeedbackFactory The requested IGraphicalFeedbackFactory
*/
protected static IGraphicalFeedbackFactory determineFeedbackFactory(
final String typeId) {
IGraphicalFeedbackFactory feedbackFactory = GraphicalFeedbackContributionsService
.getInstance().getGraphicalFeedbackFactory(typeId);
return feedbackFactory;
}
/**
* Adds a ChangeGuideCommand to the given Command.
*
* @param request
* The Request
* @param part
* The AbstractWidgetEditPart, which model should be detached
* from a guide
* @param cmd
* The Command
* @param horizontal
* A boolean, true if the guide is horizontal, false otherwise
* @return Command The given command
*/
private Command chainGuideAttachmentCommand(final Request request,
final AbstractBaseEditPart part, final Command cmd,
final boolean horizontal) {
Command result = cmd;
// Attach to guide, if one is given
Integer guidePos = (Integer) request.getExtendedData().get(
horizontal ? SnapToGuides.KEY_HORIZONTAL_GUIDE
: SnapToGuides.KEY_VERTICAL_GUIDE);
if (guidePos != null) {
int alignment = ((Integer) request.getExtendedData().get(
horizontal ? SnapToGuides.KEY_HORIZONTAL_ANCHOR
: SnapToGuides.KEY_VERTICAL_ANCHOR)).intValue();
ChangeGuideCommand cgm = new ChangeGuideCommand(part
.getWidgetModel(), horizontal);
cgm.setNewGuide(findGuideAt(guidePos.intValue(), horizontal),
alignment);
result = result.chain(cgm);
}
return result;
}
/**
* Returns the guide at the given position and with the given orientation.
*
* @param pos
* The Position of the guide
* @param horizontal
* The orientation of the guide
* @return GuideModel The GuideModel
*/
private GuideModel findGuideAt(final int pos, final boolean horizontal) {
RulerProvider provider = ((RulerProvider) getHost().getViewer()
.getProperty(
horizontal ? RulerProvider.PROPERTY_VERTICAL_RULER
: RulerProvider.PROPERTY_HORIZONTAL_RULER));
return (GuideModel) provider.getGuideAt(pos);
}
/**
* Adds a ChangeGuideCommand to the given Command.
*
* @param request
* The request
* @param part
* The AbstractWidgetEditPart, which model should be detached
* from a guide
* @param cmd
* The Command
* @param horizontal
* A boolean, true if the guide is horizontal, false otherwise
* @return Command The given command
*/
private Command chainGuideDetachmentCommand(final Request request,
final AbstractBaseEditPart part, final Command cmd,
final boolean horizontal) {
Command result = cmd;
// Detach from guide, if none is given
Integer guidePos = (Integer) request.getExtendedData().get(
horizontal ? SnapToGuides.KEY_HORIZONTAL_GUIDE
: SnapToGuides.KEY_VERTICAL_GUIDE);
if (guidePos == null) {
result = result.chain(new ChangeGuideCommand(part.getWidgetModel(),
horizontal));
}
return result;
}
/**
* Provides support for selecting, positioning, and resizing an editpart. By
* default, selection is indicated via eight square handles along the
* editpart's figure, and a rectangular handle that outlines the editpart
* with a 1-pixel black line. The eight square handles will resize the
* current selection in the eight primary directions. The rectangular handle
* will drag the current selection using a {@link
* org.eclipse.gef.tools.DragEditPartsTracker}.
* <P>
* By default, during feedback, a rectangle filled using XOR and outlined
* with dashes is drawn. This feedback can be tailored by contributing a
* {@link IGraphicalFeedbackFactory} via the extension point
* org.csstudio.sds.graphicalFeedbackFactories.
*
* @author Sven Wende
*
*/
protected final class GenericChildEditPolicy extends ResizableEditPolicy {
/**
* The edit part.
*/
private final EditPart _child;
/**
* Standard constructor.
*
* @param child
* An edit part.
*/
protected GenericChildEditPolicy(final EditPart child) {
_child = child;
}
/**
* {@inheritDoc}
*/
@Override
protected IFigure createDragSourceFeedbackFigure() {
assert _child.getModel() instanceof AbstractWidgetModel : "widget models must be derived from AbstractWidgetModel"; //$NON-NLS-1$"
String typeId = ((AbstractWidgetModel) _child.getModel())
.getTypeID();
IGraphicalFeedbackFactory feedbackFactory = determineFeedbackFactory(typeId);
IFigure feedbackFigure = feedbackFactory
.createDragSourceFeedbackFigure(
(AbstractWidgetModel) _child.getModel(),
getInitialFeedbackBounds());
addFeedback(feedbackFigure);
return feedbackFigure;
}
/**
* Shows or updates feedback for a change bounds request.
*
* @param request
* the request
*/
@Override
protected void showChangeBoundsFeedback(
final ChangeBoundsRequest request) {
assert _child.getModel() instanceof AbstractWidgetModel : "widget models must be derived from AbstractWidgetModel"; //$NON-NLS-1$"
String typeId = ((AbstractWidgetModel) _child.getModel())
.getTypeID();
IGraphicalFeedbackFactory feedbackFactory = determineFeedbackFactory(typeId);
IFigure feedbackFigure = getDragSourceFeedbackFigure();
PrecisionRectangle rect = new PrecisionRectangle(
getInitialFeedbackBounds().getCopy());
getHostFigure().translateToAbsolute(rect);
Point moveDelta = request.getMoveDelta();
rect.translate(moveDelta);
Dimension sizeDelta = request.getSizeDelta();
rect.resize(sizeDelta);
feedbackFactory.showChangeBoundsFeedback(
(AbstractWidgetModel) getHost().getModel(), rect,
feedbackFigure, request);
feedbackFigure.repaint();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected List createSelectionHandles() {
// get default handles
List handleList = super.createSelectionHandles();
// add contributed handles
assert _child.getModel() instanceof AbstractWidgetModel : "widget models must be derived from AbstractWidgetModel"; //$NON-NLS-1$"
String typeId = ((AbstractWidgetModel) _child.getModel())
.getTypeID();
IGraphicalFeedbackFactory feedbackFactory = determineFeedbackFactory(typeId);
GraphicalEditPart hostEP = (GraphicalEditPart) getHost();
List contributedHandles = feedbackFactory
.createCustomHandles(hostEP);
if (contributedHandles != null) {
handleList.addAll(contributedHandles);
}
return handleList;
}
}
/**
* {@inheritDoc}
*/
@Override
protected Dimension getMinimumSizeFor(GraphicalEditPart child) {
return new Dimension(1, 1);
}
//TODO (jhatje): is this the right way to get the relative position in AbstractConnectionEditPart?
public void getRelativePosition(Point p) {
translateFromAbsoluteToLayoutRelative(p);
}
}