/** * Copyright (c) 2009 Borland Software Corp. * * 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: * Alexander Shatalin (Borland) - initial API and implementation */ package org.eclipse.gmf.graphdef.editor.edit.policies; import java.util.Collections; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.RoundedRectangle; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.workspace.util.WorkspaceSynchronizer; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.UnexecutableCommand; import org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gef.requests.CreateRequest; import org.eclipse.gef.requests.DropRequest; import org.eclipse.gmf.gmfgraph.Alignment; import org.eclipse.gmf.gmfgraph.BorderLayoutData; import org.eclipse.gmf.gmfgraph.Figure; import org.eclipse.gmf.gmfgraph.GMFGraphFactory; import org.eclipse.gmf.gmfgraph.LayoutData; import org.eclipse.gmf.gmfgraph.RealFigure; import org.eclipse.gmf.graphdef.editor.edit.parts.AbstractFigureEditPart; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor; import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; public class BorderLayoutEditPolicy extends ConstrainedLayoutEditPolicy { private static final Color FEEDBACK_COLOR = ColorConstants.lightBlue; private RoundedRectangle myFeedbackFigure; private BorderLayoutHelper myFeedbackBoundsHelper; @Override protected void showLayoutTargetFeedback(Request request) { if (RequestConstants.REQ_CREATE.equals(request.getType()) || RequestConstants.REQ_MOVE.equals(request.getType())) { DropRequest dropRequest = (DropRequest) request; IFigure figure = getLayoutContainer(); Point where = dropRequest.getLocation().getCopy(); figure.translateToRelative(where); figure.translateFromParent(where); where.translate(getLayoutOrigin().getNegated()); if (myFeedbackBoundsHelper == null) { myFeedbackBoundsHelper = new BorderLayoutHelper((AbstractFigureEditPart) getHost()); } Rectangle feedbackBounds = myFeedbackBoundsHelper.getFeedbackBounds(where); figure.translateToParent(feedbackBounds); figure.translateToAbsolute(feedbackBounds); getFeedbackLayer().translateToRelative(feedbackBounds); if (myFeedbackFigure == null) { myFeedbackFigure = new RoundedRectangle(); myFeedbackFigure.setFill(false); myFeedbackFigure.setOutline(true); myFeedbackFigure.setForegroundColor(FEEDBACK_COLOR); myFeedbackFigure.setLineWidth(2); myFeedbackFigure.setAntialias(SWT.ON); myFeedbackFigure.setBounds(feedbackBounds); getFeedbackLayer().add(myFeedbackFigure); } else { myFeedbackFigure.setBounds(feedbackBounds); } } } @Override public void eraseTargetFeedback(Request request) { /** * RequestConstants.REQ_DROP can be used here together with * RequestConstants.REQ_MOVE due to the current * DragEditPartsTrackerEx.handleDragInProgress() implementation. * * Currently .handleDragInProgress() calls: * * 1. updateTargetRequest() setting targetRequest type to * getCommandName() (RequestConstants.REQ_MOVE) * * 2. updateTargetUnderMouse(), showTargetFeedback(), * showSourceFeedback() using current target request with the type set * on step 1. * * 3. setCurrentCommand(getCommand()); where (in addSourceCommands()) * request type will be set to RequestConstants.REQ_DROP * * So, showTargetFeedback(), showSourceFeedback() methods should be able * to react on RequestConstants.REQ_MOVE. In the same time * eraseTargetFeedback() should be sensitive to * RequestConstants.REQ_MOVE (if it was called from * updateTargetUnderMouse() then EditPart was exited) and * RequestConstants.REQ_DROP (if it was called from handleButtonUp()) * * See code for more details. */ if (RequestConstants.REQ_CREATE.equals(request.getType()) || RequestConstants.REQ_MOVE.equals(request.getType()) || RequestConstants.REQ_DROP.equals(request.getType())) { if (myFeedbackFigure != null) { getFeedbackLayer().remove(myFeedbackFigure); myFeedbackFigure = null; } myFeedbackBoundsHelper = null; } else { super.eraseTargetFeedback(request); } } @Override protected void showSizeOnDropFeedback(CreateRequest request) { Point p = new Point(request.getLocation().getCopy()); IFigure feedback = getSizeOnDropFeedback(request); feedback.translateToRelative(p); Dimension size = request.getSize().getCopy(); feedback.translateToRelative(size); feedback.setBounds(new Rectangle(p, size).expand(getCreationFeedbackOffset(request))); } @Override protected Command createChangeConstraintCommand(ChangeBoundsRequest request, EditPart child, Object constraint) { if (RequestConstants.REQ_MOVE_CHILDREN.equals(request.getType())) { Point where = request.getLocation().getCopy(); getLayoutContainer().translateToRelative(where); getLayoutContainer().translateFromParent(where); where.translate(getLayoutOrigin().getNegated()); Rectangle feedbackBounds = new BorderLayoutHelper((AbstractFigureEditPart) getHost()).getFeedbackBounds(where); return createChangeConstraintCommand(child, constraint, feedbackBounds); } else if (RequestConstants.REQ_RESIZE_CHILDREN.equals(request.getType())) { return createChangeSizeCommand(child, request); } // should never be here assert false; return null; } private Command createChangeSizeCommand(EditPart childEditPart, ChangeBoundsRequest request) { assert childEditPart instanceof AbstractFigureEditPart; AbstractFigureEditPart childFigureEditPart = (AbstractFigureEditPart) childEditPart; final RealFigure realFigure = childFigureEditPart.getRealFigure(); if (request.getSizeDelta() == null) { return null; } if (false == realFigure.getLayoutData() instanceof BorderLayoutData) { return null; } final Dimension sizeDelta = request.getSizeDelta().getCopy(); getLayoutContainer().translateToRelative(sizeDelta); final BorderLayoutData currentLayoutData = (BorderLayoutData) realFigure.getLayoutData(); if (currentLayoutData.getAlignment() == Alignment.CENTER_LITERAL || currentLayoutData.isVertical() ? sizeDelta.height == 0 : sizeDelta.width == 0) { return null; } return new ICommandProxy(new AbstractTransactionalCommand(childFigureEditPart.getEditingDomain(), "Changing preferred size", Collections.singletonList(WorkspaceSynchronizer.getFile(realFigure .eResource()))) { @Override protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { org.eclipse.gmf.gmfgraph.Dimension preferredSize = realFigure.getPreferredSize(); if (preferredSize == null) { preferredSize = GMFGraphFactory.eINSTANCE.createDimension(); realFigure.setPreferredSize(preferredSize); } if (currentLayoutData.isVertical()) { preferredSize.setDy(preferredSize.getDy() + sizeDelta.height); } else { preferredSize.setDx(preferredSize.getDx() + sizeDelta.width); } return CommandResult.newOKCommandResult(); } }); } @Override protected Command createChangeConstraintCommand(EditPart childEditPart, Object constraint) { return createChangeConstraintCommand(childEditPart, constraint, null); } // Extending default createChangeConstraintCommand method with additional // (optional) parameter - feedbackBounds protected Command createChangeConstraintCommand(EditPart childEditPart, Object constraint, final Rectangle feedbackBounds) { assert childEditPart instanceof AbstractFigureEditPart; AbstractFigureEditPart childFigureEditPart = (AbstractFigureEditPart) childEditPart; assert constraint instanceof BorderLayoutData; final BorderLayoutData layoutData = (BorderLayoutData) constraint; if (!isValidChildLayoutData(layoutData)) { return UnexecutableCommand.INSTANCE; } final RealFigure realFigure = (RealFigure) childFigureEditPart.getRealFigure(); return new ICommandProxy(new AbstractTransactionalCommand(childFigureEditPart.getEditingDomain(), "Changing Border Layout Data", Collections.singletonList(WorkspaceSynchronizer .getFile(realFigure.eResource()))) { @Override protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { realFigure.setLayoutData(layoutData); if (layoutData.getAlignment() != Alignment.CENTER_LITERAL && feedbackBounds != null) { if (realFigure.getPreferredSize() == null) { realFigure.setPreferredSize(GMFGraphFactory.eINSTANCE.createDimension()); } org.eclipse.gmf.gmfgraph.Dimension preferredSize = realFigure.getPreferredSize(); if (preferredSize.getDx() == 0) { preferredSize.setDx(feedbackBounds.width); } if (preferredSize.getDy() == 0) { preferredSize.setDy(feedbackBounds.height); } } return CommandResult.newOKCommandResult(); } }); } private boolean isValidChildLayoutData(final BorderLayoutData layoutData) { for (Figure childFigure : getRealFigure().getChildren()) { LayoutData childFigureLayoutData = childFigure.getLayoutData(); if (childFigureLayoutData instanceof BorderLayoutData) { BorderLayoutData childLayoutData = (BorderLayoutData) childFigureLayoutData; if (layoutData.isVertical() == childLayoutData.isVertical() && layoutData.getAlignment() == childLayoutData.getAlignment()) { return false; } } } return true; } @Override protected Object translateToModelConstraint(Object figureConstraint) { BorderLayoutData data = GMFGraphFactory.eINSTANCE.createBorderLayoutData(); if (org.eclipse.draw2d.BorderLayout.BOTTOM == figureConstraint) { data.setVertical(true); data.setAlignment(Alignment.END_LITERAL); } else if (org.eclipse.draw2d.BorderLayout.TOP == figureConstraint) { data.setVertical(true); data.setAlignment(Alignment.BEGINNING_LITERAL); } else if (org.eclipse.draw2d.BorderLayout.LEFT == figureConstraint) { data.setVertical(false); data.setAlignment(Alignment.BEGINNING_LITERAL); } else if (org.eclipse.draw2d.BorderLayout.RIGHT == figureConstraint) { data.setVertical(false); data.setAlignment(Alignment.END_LITERAL); } else { assert org.eclipse.draw2d.BorderLayout.CENTER == figureConstraint; data.setAlignment(Alignment.CENTER_LITERAL); } return data; } @Override protected Object getConstraintFor(ChangeBoundsRequest request, GraphicalEditPart child) { // Using request (mouse) location to determine new constraints instead // of modified child bounds as it was in super. implementation IFigure figure = getLayoutContainer(); Point where = request.getLocation().getCopy(); figure.translateToRelative(where); figure.translateFromParent(where); where.translate(getLayoutOrigin().getNegated()); return getConstraintFor(where); } @Override protected Object getConstraintFor(Point point) { return new BorderLayoutHelper((AbstractFigureEditPart) getHost()).getConstraintFor(point); } @Override protected Object getConstraintFor(Rectangle rect) { return getConstraintFor(rect.getTopLeft()); } @Override protected Command getCreateCommand(CreateRequest request) { if (false == request instanceof CreateViewRequest) { return null; } final CreateViewRequest createViewRequest = (CreateViewRequest) request; if (createViewRequest.getViewDescriptors().size() != 1) { // unable to handle several child elements creation return null; } final ViewDescriptor viewDescriptor = createViewRequest.getViewDescriptors().iterator().next(); Object modelConstraint = translateToModelConstraint(getConstraintFor(createViewRequest)); assert modelConstraint instanceof BorderLayoutData; final BorderLayoutData childLayoutData = (BorderLayoutData) modelConstraint; if (!isValidChildLayoutData(childLayoutData)) { return UnexecutableCommand.INSTANCE; } final Dimension requestedSize; if (createViewRequest.getSize() != null) { requestedSize = createViewRequest.getSize().getCopy(); getLayoutContainer().translateToRelative(requestedSize); } else { Point where = createViewRequest.getLocation().getCopy(); getLayoutContainer().translateToRelative(where); getLayoutContainer().translateFromParent(where); where.translate(getLayoutOrigin().getNegated()); Rectangle feedbackBounds = new BorderLayoutHelper((AbstractFigureEditPart) getHost()).getFeedbackBounds(where); requestedSize = feedbackBounds.getSize(); } AbstractFigureEditPart host = (AbstractFigureEditPart) getHost(); return new ICommandProxy(new AbstractTransactionalCommand(host.getEditingDomain(), "Setting BorderLayoutData for child figure", Collections.singletonList(WorkspaceSynchronizer.getFile(host .getRealFigure().eResource()))) { @Override protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { IAdaptable elementAdapter = viewDescriptor.getElementAdapter(); RealFigure realFigure = (RealFigure) elementAdapter.getAdapter(RealFigure.class); if (realFigure == null) { return CommandResult.newErrorCommandResult("Unable to locate newly created child figure"); } realFigure.setLayoutData(childLayoutData); if (childLayoutData.getAlignment() != Alignment.CENTER_LITERAL) { if (realFigure.getPreferredSize() == null) { realFigure.setPreferredSize(GMFGraphFactory.eINSTANCE.createDimension()); } org.eclipse.gmf.gmfgraph.Dimension preferredSize = realFigure.getPreferredSize(); int requestedWidth; int requestedHeight; if (createViewRequest.getSize() != null) { requestedWidth = requestedSize.width; requestedHeight = requestedSize.height; } else { if (preferredSize.getDx() == 0) { requestedWidth = requestedSize.width; } else { requestedWidth = preferredSize.getDx(); } if (preferredSize.getDy() == 0) { requestedHeight = requestedSize.height; } else { requestedHeight = preferredSize.getDy(); } } if (preferredSize.getDy() != requestedHeight) { preferredSize.setDy(requestedHeight); } if (preferredSize.getDx() != requestedWidth) { preferredSize.setDx(requestedWidth); } } return CommandResult.newOKCommandResult(); } }); } @Override protected EditPolicy createChildEditPolicy(EditPart childEditPart) { assert childEditPart instanceof AbstractFigureEditPart; AbstractFigureEditPart childFigureEditPart = (AbstractFigureEditPart) childEditPart; LayoutData layoutData = childFigureEditPart.getRealFigure().getLayoutData(); if (false == layoutData instanceof BorderLayoutData) { return null; } BorderLayoutData borderLayoutData = (BorderLayoutData) layoutData; ResizableEditPolicy result = new ResizableEditPolicy(); if (borderLayoutData.getAlignment() == Alignment.CENTER_LITERAL) { result.setResizeDirections(PositionConstants.NONE); } else if (borderLayoutData.getAlignment() == Alignment.BEGINNING_LITERAL) { result.setResizeDirections(borderLayoutData.isVertical() ? PositionConstants.SOUTH : PositionConstants.EAST); } else { // borderLayoutData.getAlignment() == Alignment.END_LITERAL result.setResizeDirections(borderLayoutData.isVertical() ? PositionConstants.NORTH : PositionConstants.WEST); } return result; } private RealFigure getRealFigure() { return ((AbstractFigureEditPart) getHost()).getRealFigure(); } }