/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.gef.editpolicies;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.TreeEditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.DropRequest;
/**
* An EditPolicy for handling ADDS, MOVES, and CREATES on a {@link TreeEditPart}.
* <P>
* This EditPolicy is responsible for displaying the insertion feedback in the Tree during
* the appropriate interactions.
* <P>
* This EditPolicy factors the {@link #getCommand(Request)} into three different abstract
* methods which subclasses must implement.
* @since 2.0
*/
public abstract class TreeContainerEditPolicy
extends AbstractEditPolicy
{
private static TreeItem[] oldSelection;
/**
* Returns a Command for adding the children to the container.
* @param request the Request to add.
* @return Command <code>null</code> or a Command to perform the add
*/
protected abstract Command getAddCommand(ChangeBoundsRequest request);
/**
* Returns a Command for creating the object inside the container.
* @param request the CreateRequest
* @return Command <code>null</code> or a Command to perform the create
*/
protected abstract Command getCreateCommand(CreateRequest request);
/**
* Returns a Command for moving the children within the container.
* @param request the Request to move
* @return Command <code>null</code> or a Command to perform the move
*/
protected abstract Command getMoveChildrenCommand(ChangeBoundsRequest request);
private void eraseDropFeedback(Request req) {
getTree().setInsertMark(null, true);
restoreSelection();
}
/**
* @see org.eclipse.gef.EditPolicy#eraseTargetFeedback(Request)
*/
public void eraseTargetFeedback(Request req) {
if (req.getType().equals(REQ_MOVE)
|| req.getType().equals(REQ_ADD)
|| req.getType().equals(REQ_CREATE))
eraseDropFeedback(req);
}
/**
* Calculates the index of the TreeItem at given point.
* @param pt the Point in the Viewer
* @return the index of the TreeItem
*/
protected final int findIndexOfTreeItemAt(org.eclipse.draw2d.geometry.Point pt) {
int index = -1;
TreeItem item = findTreeItemAt(pt);
if (item != null) {
index = getHost().getChildren().indexOf(item.getData());
if (index >= 0 && !isInUpperHalf(item.getBounds(), pt))
index++;
}
return index;
}
/**
* Calculates the <code>TreeItem</code> at a specified {@link
* org.eclipse.draw2d.geometry.Point}.
* @param pt the draw2d Point
* @return <code>null</code> or the TreeItem
*/
protected final TreeItem findTreeItemAt(org.eclipse.draw2d.geometry.Point pt) {
return getTree().getItem(new Point(pt.x, pt.y));
}
/**
* @see org.eclipse.gef.EditPolicy#getCommand(Request)
*/
public Command getCommand(Request req) {
if (req.getType().equals(REQ_MOVE_CHILDREN))
return getMoveChildrenCommand((ChangeBoundsRequest)req);
if (req.getType().equals(REQ_ADD))
return getAddCommand((ChangeBoundsRequest)req);
if (req.getType().equals(REQ_CREATE))
return getCreateCommand((CreateRequest)req);
return null;
}
/**
* Returns the host EditPart when appropriate. Targeting is done by checking if the mouse
* is clearly over the host's TreeItem.
* @see org.eclipse.gef.EditPolicy#getTargetEditPart(Request)
*/
public EditPart getTargetEditPart(Request req) {
if (req.getType().equals(REQ_ADD)
|| req.getType().equals(REQ_MOVE)
|| req.getType().equals(REQ_CREATE)) {
DropRequest drop = (DropRequest) req;
Point where = new Point(drop.getLocation().x, drop.getLocation().y);
Widget widget = ((TreeEditPart) getHost()).getWidget();
if (widget instanceof Tree)
return getHost();
TreeItem treeitem = (TreeItem) widget;
Rectangle bounds = treeitem.getBounds();
int fudge = bounds.height / 5;
Rectangle inner =
new Rectangle(
bounds.x,
bounds.y + fudge,
bounds.width,
bounds.height - (treeitem.getExpanded() ? 0 : fudge * 2));
//Point is either outside the Treeitem, or inside the inner Rect.
if (!bounds.contains(where) || inner.contains(where))
return getHost();
}
return null;
}
private Tree getTree() {
Widget widget = ((TreeEditPart)getHost()).getWidget();
if (widget instanceof Tree)
return (Tree)widget;
else
return ((TreeItem)widget).getParent();
}
private void insertMarkAfterLastChild(TreeItem[] children) {
if (children != null && children.length > 0) {
TreeItem item = children[ children.length - 1 ];
getTree().setInsertMark(item, false);
}
}
private boolean isInUpperHalf(Rectangle rect,
org.eclipse.draw2d.geometry.Point pt) {
Rectangle tempRect = new Rectangle(rect.x, rect.y,
rect.width, rect.height / 2);
return tempRect.contains(new Point(pt.x, pt.y));
}
private void restoreSelection() {
if (oldSelection != null) {
try {
getTree().setSelection(oldSelection);
} finally {
oldSelection = null;
}
}
}
private void showDropFeedback(DropRequest request) {
Widget hostWidget = ((TreeEditPart)getHost()).getWidget();
Tree tree = getTree();
if (oldSelection == null) {
oldSelection = tree.getSelection();
TreeItem newSelection[];
if (hostWidget instanceof Tree) {
newSelection = new TreeItem[0];
} else {
/*
* if parent item is out of the visible bounds, do not select
* it, as the selection causes the viewer to scroll which
* changes the insert location.
*/
TreeItem item = (TreeItem) hostWidget;
if(item.getBounds().y < 0) {
oldSelection = null;
newSelection = new TreeItem[0];
} else {
newSelection = new TreeItem[] { item };
}
}
tree.setSelection(newSelection);
}
org.eclipse.draw2d.geometry.Point pt = request.getLocation();
TreeItem item = findTreeItemAt(pt);
if (item == null) {
if (hostWidget == tree) {
insertMarkAfterLastChild(tree.getItems());
}
} else if (item == hostWidget) {
tree.setInsertMark(null, true);
} else {
boolean before = isInUpperHalf(item.getBounds(), pt);
tree.setInsertMark(item, before);
}
}
/**
* @see org.eclipse.gef.EditPolicy#showTargetFeedback(Request)
*/
public void showTargetFeedback(Request req) {
if (req.getType().equals(REQ_MOVE)
|| req.getType().equals(REQ_ADD)
|| req.getType().equals(REQ_CREATE))
showDropFeedback((DropRequest)req);
}
}