/******************************************************************************* * Copyright (c) 2012 Olivier Moises * * 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: * Olivier Moises- initial API and implementation *******************************************************************************/ package org.eclipse.wazaabi.ide.ui.editors.viewer; import java.util.Collection; import java.util.Collections; import org.eclipse.draw2d.geometry.Point; import org.eclipse.emf.edit.ui.dnd.LocalTransfer; import org.eclipse.gef.AutoexposeHelper; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.TreeEditPart; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.jface.util.TransferDropTargetListener; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.dnd.TransferData; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; public abstract class AbstractTransferDropTargetListener implements TransferDropTargetListener { private static Boolean isLinux = null; private final EditPartViewer viewer; private AutoexposeHelper exposeHelper; private boolean hovering = false; private long hoverStartTime = -1; private Point prevMouseLoc; private boolean showingFeedback; private EditPart target; protected EditPartViewer getViewer() { return viewer; } public AbstractTransferDropTargetListener(EditPartViewer viewer) { this.viewer = viewer; } public void dragEnter(DropTargetEvent event) { resetHover(); } public void dragLeave(DropTargetEvent event) { unload(event); } public void dragOperationChanged(DropTargetEvent event) { resetHover(); // setCurrentEvent(event); handleDragOperationChanged(event); } /** * Tests whether the given event's location is different than the previous * event's location, and sets the remembered location to the current event's * location. * * @param event * @return boolean */ private boolean testAndSet(DropTargetEvent event) { boolean result = prevMouseLoc == null || !(prevMouseLoc.x == event.x && prevMouseLoc.y == event.y); if (prevMouseLoc == null) prevMouseLoc = new Point(); prevMouseLoc.x = event.x; prevMouseLoc.y = event.y; return result; } public void dragOver(DropTargetEvent event) { handleDragOver(event); if (testAndSet(event)) { resetHover(); } else { if (hovering) return; long currentTime = event.time; if (hoverStartTime == -1) { hoverStartTime = currentTime; } else if (currentTime - hoverStartTime > 400) { handleHover(event); hovering = true; } } } /** * Called whenever the User drags over the target. By default, the target * Request and target EditPart are updated, feedback is shown, and * auto-expose occurs. */ protected void handleDragOver(DropTargetEvent event) { showTargetFeedback(event); if (exposeHelper != null) { // If the expose helper does not wish to continue, set helper to // null. if (!exposeHelper.step(getDropLocation(event))) exposeHelper = null; } } /** * Updates the active {@link AutoexposeHelper}. Does nothing if there is * still an active helper. Otherwise, obtains a new helper (possible * <code>null</code>) at the current mouse location and calls * {@link #setAutoexposeHelper(AutoexposeHelper)}. */ protected void updateAutoexposeHelper(DropTargetEvent event) { if (exposeHelper != null) return; AutoexposeHelper.Search search; search = new AutoexposeHelper.Search(getDropLocation(event)); getViewer().findObjectAtExcluding(getDropLocation(event), Collections.EMPTY_LIST, search); setAutoexposeHelper(search.result); } /** * Sets the current autoexpose helper. * * @param helper * the autoexpose helper */ protected void setAutoexposeHelper(AutoexposeHelper helper) { exposeHelper = helper; } private void resetHover() { if (hovering) { handleHoverStop(); hovering = false; hoverStartTime = -1; prevMouseLoc = null; } } protected void showTargetFeedback(DropTargetEvent event) { if (getTargetEditPart() != null) { showingFeedback = true; showDropFeedback(getTargetEditPart(), getDropLocation(event)); } } protected void eraseTargetFeedback(DropTargetEvent event) { if (getTargetEditPart() != null && showingFeedback) { showingFeedback = false; eraseDropFeedback(getTargetEditPart(), getDropLocation(event)); } } /** * Called when the mouse resumes motion after having hovered. */ protected void handleHoverStop() { } /** * Called when the mouse hovers during drag and drop. */ protected void handleHover(DropTargetEvent event) { updateAutoexposeHelper(event); } public void drop(DropTargetEvent event) { // setCurrentEvent(event); eraseTargetFeedback(event); handleDrop(event); unload(event); } protected void handleDrop(DropTargetEvent event) { // updateTargetRequest(); // updateTargetEditPart(); Command command = getCommand(event); if (command != null && command.canExecute()) getViewer().getEditDomain().getCommandStack().execute(command); else event.detail = DND.DROP_NONE; } /** * Erases target feedback and sets the request to <code>null</code>. */ protected void unload(DropTargetEvent event) { resetHover(); eraseTargetFeedback(event); setTargetEditPart(null); setAutoexposeHelper(null); } protected void handleDragOperationChanged(DropTargetEvent event) { // Erase any old feedback now, in case the request changes substantially eraseTargetFeedback(event); // Update request based on the new operation type // updateTargetRequest(); // Update the target based on the updated request // updateTargetEditPart(); } public void dropAccept(DropTargetEvent event) { System.out.println("drop accept"); } public Transfer getTransfer() { return LocalTransfer.getInstance(); } protected abstract Object getObjects(final TransferData transferData); protected final TreeItem findTreeItemAt(Point pt) { return ((Tree) getViewer().getControl()) .getItem(new org.eclipse.swt.graphics.Point(pt.x, pt.y)); } protected final int findIndexOfTreeItemAt(TreeEditPart editpart, Point pt) { int index = -1; TreeItem item = findTreeItemAt(pt); if (item != null) { index = editpart.getChildren().indexOf(item.getData()); if (index >= 0 && !isInUpperHalf(item.getBounds(), pt)) index++; } return index; } private boolean isInUpperHalf(org.eclipse.swt.graphics.Rectangle rect, org.eclipse.draw2d.geometry.Point pt) { org.eclipse.swt.graphics.Rectangle tempRect = new org.eclipse.swt.graphics.Rectangle( rect.x, rect.y, rect.width, rect.height / 2); return tempRect .contains(new org.eclipse.swt.graphics.Point(pt.x, pt.y)); } public boolean isEnabled(DropTargetEvent event) { if (isLinux()) return true; Command command = getCommand(event); return command != null ? command.canExecute() : false; } protected Command getCommand(DropTargetEvent event) { CompoundCommand compoundCommand = new CompoundCommand(); for (int i = 0; i < event.dataTypes.length; i++) { if (getTransfer().isSupportedType(event.dataTypes[i])) { TreeEditPart underMouseEditPart = findTargetEditPartUnderMouse(event); if (underMouseEditPart == null) continue; event.currentDataType = event.dataTypes[i]; Object source = null; if (isLinux()) { if (event.data instanceof TreeSelection && !((TreeSelection) event.data).isEmpty()) source = ((TreeSelection) event.data).getFirstElement(); } else source = getObjects(event.currentDataType); TreeEditPart targetEditPart = getTargetEditPart( findTargetEditPartUnderMouse(event), source, event); setTargetEditPart(targetEditPart); if (targetEditPart != null) { int index = findIndexOfTreeItemAt(targetEditPart, getDropLocation(event)); Command command = getCommand(targetEditPart, source, getDomainIndexOf(index, targetEditPart, source)); if (command != null) compoundCommand.add(command); } } } if (compoundCommand.size() == 1) return (Command) compoundCommand.getCommands().get(0); if (compoundCommand.isEmpty()) return null; return compoundCommand; } protected abstract int getDomainIndexOf(int index, TreeEditPart targetEditPart, Object source); protected Point getDropLocation(DropTargetEvent event) { org.eclipse.swt.graphics.Point swt; swt = new org.eclipse.swt.graphics.Point(event.x, event.y); DropTarget target = (DropTarget) event.widget; swt = target.getControl().toControl(swt); return new Point(swt.x, swt.y); } protected Collection<?> getExclusionSet(DropTargetEvent event) { return Collections.EMPTY_LIST; } private TreeEditPart findTargetEditPartUnderMouse(DropTargetEvent event) { EditPart ep = getViewer().findObjectAtExcluding(getDropLocation(event), getExclusionSet(event), null); if (ep instanceof TreeEditPart) return (TreeEditPart) ep; return null; } protected abstract Command getCommand(TreeEditPart target, Object source, int index); public abstract TreeEditPart getTargetEditPart( TreeEditPart underMouseEditPart, Object source, DropTargetEvent event); private void showDropFeedback(EditPart editPart, Point pt) { if (!(editPart instanceof TreeEditPart)) return; Widget hostWidget = ((TreeEditPart) editPart).getWidget(); Tree tree = (Tree) getViewer().getControl(); 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); } } private void insertMarkAfterLastChild(TreeItem[] children) { if (children != null && children.length > 0) { TreeItem item = children[children.length - 1]; ((Tree) getViewer().getControl()).setInsertMark(item, false); } } private void eraseDropFeedback(EditPart targetEditpart, Point pt) { ((Tree) getViewer().getControl()).setInsertMark(null, true); } protected void setTargetEditPart(EditPart ep) { if (ep != target) { if (target != null) handleExitingEditPart(ep); target = ep; if (target != null) handleEnteredEditPart(ep); } } /** * Returns the current <i>target</i> <code>EditPart</code>. * * @return the target EditPart */ protected EditPart getTargetEditPart() { return target; } protected void handleEnteredEditPart(EditPart ep) { } /** * Called as the current target EditPart is being exited. By default, the * target is asked to erase feedback. */ protected void handleExitingEditPart(EditPart ep) { eraseDropFeedback(ep, null); } protected boolean isLinux() { if (isLinux == null) { String OS = System.getProperty("os.name").toLowerCase(); isLinux = (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS .indexOf("aix") > 0); } assert isLinux != null; return isLinux; } }