/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.ui.internal.tools;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Layer;
import org.eclipse.draw2d.UpdateManager;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.xmind.gef.GEF;
import org.xmind.gef.Request;
import org.xmind.gef.draw2d.DecoratedShapeFigure;
import org.xmind.gef.draw2d.IReferencedFigure;
import org.xmind.gef.draw2d.decoration.PathShapeDecoration;
import org.xmind.gef.draw2d.graphics.Path;
import org.xmind.gef.event.DragDropEvent;
import org.xmind.gef.event.KeyEvent;
import org.xmind.gef.event.MouseEvent;
import org.xmind.gef.graphicalpolicy.IStructure;
import org.xmind.gef.part.IPart;
import org.xmind.gef.tool.GraphicalTool;
import org.xmind.ui.branch.IInsertableBranchStructureExtension;
import org.xmind.ui.branch.ILockableBranchStructureExtension;
import org.xmind.ui.branch.IMovableBranchStructureExtension;
import org.xmind.ui.mindmap.IBranchPart;
import org.xmind.ui.mindmap.ISheetPart;
import org.xmind.ui.mindmap.ITopicPart;
import org.xmind.ui.mindmap.MindMapUI;
import org.xmind.ui.style.Styles;
import org.xmind.ui.tools.ITopicMoveToolHelper;
import org.xmind.ui.tools.ParentSearchKey;
import org.xmind.ui.tools.ParentSearcher;
public class MindMapDndTool extends GraphicalTool {
private class RoundedRectDecoration extends PathShapeDecoration {
public Insets getPreferredInsets(IFigure figure, int width,
int height) {
int lineWidth = getLineWidth();
return new Insets(lineWidth, lineWidth, lineWidth, lineWidth);
}
@Override
protected void sketch(IFigure figure, Path shape, Rectangle box,
int purpose) {
shape.addRoundedRectangle(box, 5);
}
}
private static ITopicMoveToolHelper defaultHelper = null;
private BranchDummy dummy = null;
private IFigure invent = null;
private IBranchPart targetParent = null;
private ParentSearcher parentSearcher = null;
private ParentSearchKey key = null;
private boolean insideTopicAllowed = false;
private ITopicMoveToolHelper helper = null;
private Request request = null;
protected boolean acceptEvent(DragDropEvent de) {
return true;
}
protected boolean handleDragStarted(DragDropEvent de) {
targetParent = null;
request = null;
insideTopicAllowed = isInsideTopicAllowed(de);
if (acceptEvent(de)) {
createDummy(de);
lockBranchStructures(getTargetViewer().getRootPart());
return true;
}
return false;
}
private void lockBranchStructures(IPart part) {
if (part instanceof IBranchPart) {
IBranchPart branch = (IBranchPart) part;
IStructure sa = branch.getBranchPolicy().getStructure(branch);
if (sa instanceof ILockableBranchStructureExtension) {
((ILockableBranchStructureExtension) sa).lock(branch);
}
}
for (IPart child : part.getChildren()) {
lockBranchStructures(child);
}
}
private void unlockBranchStructures(IPart part) {
if (part instanceof IBranchPart) {
IBranchPart branch = (IBranchPart) part;
IStructure sa = branch.getBranchPolicy().getStructure(branch);
if (sa instanceof ILockableBranchStructureExtension) {
((ILockableBranchStructureExtension) sa).unlock(branch);
}
}
for (IPart child : part.getChildren()) {
unlockBranchStructures(child);
}
}
private void createDummy(DragDropEvent de) {
if (dummy != null) {
dummy.dispose();
dummy = null;
}
dummy = new BranchDummy(getTargetViewer(), false);
decorateDummy(dummy, de);
}
protected void decorateDummy(BranchDummy dummy, DragDropEvent de) {
dummy.setStyle(Styles.ShapeClass, Styles.TOPIC_SHAPE_NO_BORDER);
dummy.getTopic().setTitleText(""); //$NON-NLS-1$
dummy.getBranch().refresh();
}
public IFigure getInvent() {
return invent;
}
private void doCreateInvent() {
if (invent != null)
return;
invent = createInvent();
}
private IFigure createInvent() {
DecoratedShapeFigure figure = new DecoratedShapeFigure();
figure.setSize(60, 15);
figure.setDecoration(new RoundedRectDecoration());
Layer layer = getTargetViewer().getLayer(GEF.LAYER_PRESENTATION);
if (layer != null)
layer.add(figure);
return figure;
}
private void destroyInvent() {
if (invent != null) {
if (invent.getParent() != null)
invent.getParent().remove(invent);
invent = null;
}
}
protected boolean isInsideTopicAllowed(DragDropEvent de) {
return true;
}
protected boolean handleDragOver(DragDropEvent de) {
if (acceptEvent(de)) {
if (dummy != null) {
key = new ParentSearchKey(
null, (IReferencedFigure) dummy.getBranch()
.getTopicPart().getFigure(),
getCursorPosition());
key.setFeedback(dummy.getBranch());
targetParent = updateTargetParent();
if (invent == null)
doCreateInvent();
key.setInvent(invent);
updateWithParent(targetParent);
return true;
}
}
return false;
}
private IBranchPart updateTargetParent() {
return getParentSearcher()
.searchTargetParent(getTargetViewer().getRootPart(), key);
}
private void updateWithParent(IBranchPart parent) {
updateDummyWithParent(parent);
updateHelperWithParent(parent);
}
private void updateDummyWithParent(IBranchPart parent) {
updateDummyPosition(getCursorPosition());
updateInventPosition(getCursorPosition());
updateInventVisible(parent);
}
private void updateInventVisible(IBranchPart parent) {
if (invent != null)
invent.setVisible(parent != null && !parent.getTopicPart()
.getFigure().containsPoint(getCursorPosition()));
}
private void updateInventPosition(Point cursorPosition) {
IFigure fig = getInvent();
Point pos = new Point();
if (fig != null) {
if (targetParent != null && key != null) {
pos = calcInsertionPosition(targetParent, key);
}
if (fig instanceof IReferencedFigure)
((IReferencedFigure) fig).setReference(pos);
else
fig.setLocation(pos);
}
}
private Point calcInsertionPosition(IBranchPart parent,
ParentSearchKey key) {
UpdateManager um = key.getFigure().getUpdateManager();
if (um != null)
um.performValidation();
if (parent != null) {
IStructure structure = parent.getBranchPolicy()
.getStructure(parent);
if (structure instanceof IInsertableBranchStructureExtension)
return ((IInsertableBranchStructureExtension) structure)
.calcInsertionPosition(parent, null, key);
}
return new Point();
}
protected void updateDummyPosition(Point pos) {
IFigure fig = dummy.getBranch().getFigure();
if (fig != null) {
if (fig instanceof IReferencedFigure) {
((IReferencedFigure) fig).setReference(pos);
} else {
fig.setLocation(pos);
}
}
}
private void updateHelperWithParent(IBranchPart parent) {
ITopicMoveToolHelper oldHelper = this.helper;
ITopicMoveToolHelper newHelper = getHelper(parent);
if (newHelper != oldHelper) {
if (oldHelper != null)
oldHelper.deactivate(getDomain(), getTargetViewer());
if (newHelper != null)
newHelper.activate(getDomain(), getTargetViewer());
this.helper = newHelper;
}
if (helper != null) {
helper.update(parent, key);
}
}
private ITopicMoveToolHelper getHelper(IBranchPart parent) {
return getDefaultHelper();
}
protected static ITopicMoveToolHelper getDefaultHelper() {
if (defaultHelper == null) {
defaultHelper = new TopicMoveToolHelper();
}
return defaultHelper;
}
protected ParentSearcher getParentSearcher() {
if (parentSearcher == null) {
parentSearcher = new ParentSearcher(insideTopicAllowed);
}
return parentSearcher;
}
protected boolean handleDragDismissed(DragDropEvent de) {
if (acceptEvent(de)) {
request = createRequest(de);
destroyDummy();
destroyInvent();
changeActiveTool(GEF.TOOL_DEFAULT);
return true;
}
return false;
}
private IPart findDropTarget(IPart target) {
if (target == null)
return null;
if (target.hasRole(GEF.ROLE_DROP_TARGET))
return target;
return findDropTarget(target.getParent());
}
private Request createRequest(DragDropEvent de) {
Request req = new Request(GEF.REQ_DROP);
IPart target = findDropTarget(de.target);
if (target == null) {
target = (ISheetPart) getTargetViewer()
.getAdapter(ISheetPart.class);
}
req.setPrimaryTarget(target);
ITopicPart targetTopic = targetParent == null ? null
: targetParent.getTopicPart();
if (targetTopic != null) {
req.setParameter(GEF.PARAM_PARENT, targetTopic);
int targetIndex = -1;
if (targetParent != null) {
targetIndex = getParentSearcher().getIndex(targetParent, key);
}
req.setParameter(GEF.PARAM_INDEX, targetIndex);
req.setParameter(GEF.PARAM_POSITION_ABSOLUTE, getCursorPosition());
}
if (targetParent == null) {
Point position = getCursorPosition();
req.setParameter(GEF.PARAM_POSITION, position);
}
req.setParameter(GEF.PARAM_DROP_OPERATION, de.detail);
if (targetParent != null) {
IStructure structure = targetParent.getBranchPolicy()
.getStructure(targetParent);
if (structure instanceof IMovableBranchStructureExtension) {
((IMovableBranchStructureExtension) structure)
.decorateMoveInRequest(targetParent, key, null, req);
}
}
return req;
}
private void destroyDummy() {
unlockBranchStructures(getTargetViewer().getRootPart());
ITopicMoveToolHelper oldHelper = this.helper;
BranchDummy oldDummy = this.dummy;
if (oldHelper != null) {
oldHelper.deactivate(getDomain(), getTargetViewer());
}
if (oldDummy != null) {
oldDummy.dispose();
}
this.helper = null;
this.dummy = null;
}
protected boolean handleDrop(DragDropEvent de) {
try {
if (acceptEvent(de)) {
final Request req = this.request;
if (req != null) {
req.setParameter(MindMapUI.PARAM_DND_DATA, de.dndData);
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
SafeRunner.run(new SafeRunnable() {
public void run() throws Exception {
BusyIndicator.showWhile(
Display.getCurrent(),
new Runnable() {
public void run() {
getDomain().handleRequest(req);
}
});
}
});
}
});
return true;
}
}
return false;
} finally {
this.request = null;
}
}
@Override
protected boolean handleKeyTraversed(KeyEvent ke) {
if (ke.traverse == SWT.TRAVERSE_ESCAPE) {
destroyDummy();
destroyInvent();
changeActiveTool(GEF.TOOL_DEFAULT);
ke.consume();
return true;
}
return super.handleKeyTraversed(ke);
}
private boolean mouseDown = false;
@Override
protected boolean handleMouseDown(MouseEvent me) {
mouseDown = true;
return super.handleMouseDown(me);
}
@Override
protected boolean handleMouseUp(MouseEvent me) {
if (mouseDown) {
mouseDown = false;
destroyDummy();
destroyInvent();
changeActiveTool(GEF.TOOL_DEFAULT);
return true;
}
return super.handleMouseUp(me);
}
}