/* * $Id$ * * Copyright (c) 2004-2005 by the TeXlapse Team. * 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 */ package net.sourceforge.texlipse.outline; import net.sourceforge.texlipse.TexlipsePlugin; import net.sourceforge.texlipse.model.OutlineNode; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerDropAdapter; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.TransferData; /** * The Drag'n'Drop adapter for TexOutline. All the drag'n'drop * operations are text based. * * Since the Positions do not seem to get updated fast enough * the drag source position is calculated manually. * * @author Taavi Hupponen * */ public class TexOutlineDNDAdapter extends ViewerDropAdapter implements DragSourceListener { private TexOutlinePage outline; private OutlineNode dragSource; private int removeOffset; public TexOutlineDNDAdapter(Viewer viewer, TexOutlinePage outlinePage) { super(viewer); this.outline = outlinePage; this.setFeedbackEnabled(false); } /** * Perform the drop. Also calculate the source remove offset before * actual dropping. * * @see org.eclipse.jface.viewers.ViewerDropAdapter#performDrop(java.lang.Object) */ public boolean performDrop(Object data) { // get target and calculate remove offset if target is // above source OutlineNode target = (OutlineNode)this.getCurrentTarget(); int targetOffset = target.getPosition().getOffset() + target.getPosition().getLength(); int sourceOffset = this.dragSource.getPosition().getOffset(); if (targetOffset < sourceOffset) { removeOffset = sourceOffset + ((String)data).length(); } else { removeOffset = sourceOffset; } // drop return false if fails try { getDocument().replace(targetOffset, 0, (String)data); } catch (BadLocationException e) { TexlipsePlugin.log("Could not perform drop operation.", e); return false; } return true; } /** * Validate the drop. Invalidation is caused if * * - outline is not uptodate * - transfer type is other than text * - drop target is equal or children of source * - target is preamble * * @param target * @param operation * @param transferType * * @return true if drop is valid * * @see org.eclipse.jface.viewers.ViewerDropAdapter#validateDrop(java.lang.Object, int, org.eclipse.swt.dnd.TransferData) */ public boolean validateDrop(Object target, int operation, TransferData transferType) { // deny if outline is dirty if (this.outline.isModelDirty()) { return false; } // check transfer type, only allow text if (!TextTransfer.getInstance().isSupportedType(transferType)) { return false; } // get the selected node, check null and type OutlineNode targetNode = (OutlineNode)target; if (targetNode == null || targetNode.getType() == OutlineNode.TYPE_PREAMBLE) { return false; } // deny dropping over oneself or over ones children if (targetNode.equals(this.dragSource) || isAncestor(dragSource, targetNode)) { return false; } return true; } /** * Validate the drag start. Dragging is denied if: * * - outline is not uptodate * - source is preamble * * @param event the drag event * @see org.eclipse.swt.dnd.DragSourceListener#dragStart(org.eclipse.swt.dnd.DragSourceEvent) */ public void dragStart(DragSourceEvent event) { event.doit = false; // deny if outline is dirty if (this.outline.isModelDirty()) { return; } // get the selected node OutlineNode node = this.getSelection(); if (node == null) { return; } // deny dragging of certain elements if (node.getType() == OutlineNode.TYPE_PREAMBLE) { return; } // proceed this.dragSource = node; event.doit = true; } /** * Set the text data into TextTransfer. * * @see org.eclipse.swt.dnd.DragSourceListener#dragSetData(org.eclipse.swt.dnd.DragSourceEvent) */ public void dragSetData(DragSourceEvent event) { // check that requested data type is supported if (!TextTransfer.getInstance().isSupportedType(event.dataType)) { return; } // get the source text int sourceOffset = this.dragSource.getPosition().getOffset(); int sourceLength = this.dragSource.getPosition().getLength(); Position sourcePosition = dragSource.getPosition(); String sourceText = ""; try { sourceText = getDocument().get(sourcePosition.getOffset(), sourcePosition.getLength()); } catch (BadLocationException e) { TexlipsePlugin.log("Could not set drag data.", e); return; } // set the data event.data = sourceText; } /** * Finish the drag by removing the source text if the operation * was MOVE. * * Trigger updating of TexlipseModel and outline when done. * * @param event the dragEvent * @see org.eclipse.swt.dnd.DragSourceListener#dragFinished(org.eclipse.swt.dnd.DragSourceEvent) */ public void dragFinished(DragSourceEvent event) { // remove MOVE source if (event.detail == DND.DROP_MOVE) { int sourceLength = this.dragSource.getPosition().getLength(); try { getDocument().replace(removeOffset, sourceLength, ""); } catch (BadLocationException e) { TexlipsePlugin.log("Could not remove drag'n'drop source.", e); } } // trigger parsing this.outline.getEditor().updateModelNow(); } /** * Helper for getting the IDocument. * * @return the IDocument assosiated with the outline. */ private IDocument getDocument() { return outline.getEditor().getDocumentProvider().getDocument(outline.getEditor().getEditorInput()); } /** * Helper for getting the current selection in the viewer. * * @return currently selected OutlineNode or null if none is selected */ private OutlineNode getSelection() { ISelection selection = this.getViewer().getSelection(); if (selection == null) { return null; } return (OutlineNode)((IStructuredSelection)selection).getFirstElement(); } /** * Returns whether the given ancestor is ancestor to the given child. * * @param ancestor * @param child * @return true if ancestor is ancestor for the given child */ private boolean isAncestor(OutlineNode ancestor, OutlineNode child) { OutlineNode parent = child.getParent(); if (parent == null) { return false; } else if (parent.equals(ancestor)) { return true; } else { return isAncestor(ancestor, parent); } } }