diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 80ef8e7ea52..be15b0addd1 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -40,6 +40,11 @@ + + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties index e070a2bd5d1..53aac83e8d3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties @@ -8,6 +8,7 @@ # Contributors: # Rational Software - Initial API and implementation # Markus Schorn (Wind River Systems) +# Anton Leherbauer (Wind River Systems) ############################################################################### Drag.move.problem.title=Drag and Drop Problem @@ -89,3 +90,8 @@ CHelpConfigurationPropertyPage.buttonLabels.UncheckAll=Uncheck All CHelpConfigurationPropertyPage.HelpBooks=Help books AsyncTreeContentProvider.JobName=Child Node Computation AsyncTreeContentProvider.TaskName=Compute child nodes + +TextEditorDropAdapter.error.title=Drag and Drop +TextEditorDropAdapter.error.message=A problem occurred during drag and drop. +TextEditorDropAdapter.unreadableFile=Unreadable file: ''{0}'' +TextEditorDropAdapter.noFile=Not a file: ''{0}'' diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dnd/TextEditorDropAdapter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dnd/TextEditorDropAdapter.java new file mode 100644 index 00000000000..daf1c1b62ec --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dnd/TextEditorDropAdapter.java @@ -0,0 +1,558 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.dnd; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.jface.text.Position; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.StyledTextContent; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetAdapter; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.FileTransfer; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Caret; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.part.MarkerTransfer; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.ITextEditorDropTargetListener; +import org.eclipse.ui.texteditor.ITextEditorExtension; + +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.CUIMessages; +import org.eclipse.cdt.internal.ui.util.EditorUtility; +import org.eclipse.cdt.internal.ui.util.ExceptionHandler; + +/** + * A drop adapter which supports dragging a non-workspace file from some + * external tool (e.g. explorer) into the editor area. The adaptor also supports + * text and marker transfer. + */ +public class TextEditorDropAdapter extends DropTargetAdapter implements + ITextEditorDropTargetListener { + + /** + * Adapter factory for text editor drop target listeners. Can be registered + * to add text, file and marker drop support for all + * ITextEditors. + * + * @see ITextEditorDropTargetListener + */ + public static class Factory implements IAdapterFactory { + private static final Class[] CLASSES= { ITextEditorDropTargetListener.class }; + + /* + * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, + * java.lang.Class) + */ + public Object getAdapter(Object adaptableObject, Class adapterType) { + if (adaptableObject instanceof ITextEditor) { + return TextEditorDropAdapter.create((ITextEditor) adaptableObject); + } + return null; + } + + /* + * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList() + */ + public Class[] getAdapterList() { + return CLASSES; + } + + } + + /** Saved original caret during drag operation */ + private Caret fOldCaret; + /** Caret used during drag operation to indicate insert point */ + private Caret fCaret; + /** The text viewer target */ + private ITextViewer fViewer; + /** The editor containing the viewer (can be null) */ + private ITextEditor fEditor; + /** Direction of current autoscroll SWT.UP, SWT.DOWN or SWT.NULL */ + private int fAutoScrollDirection= SWT.NULL; + + /** + * Create an EditorDropAdapter for the given text viewer and (optional) + * editor. + * + * @param viewer + * the text viewer, may not be null + * @param editor + * the text editor, may be null + */ + public TextEditorDropAdapter(ITextViewer viewer, ITextEditor editor) { + super(); + fViewer= viewer; + fEditor= editor; + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#drop(org.eclipse.swt.dnd.DropTargetEvent) + */ + public void drop(final DropTargetEvent event) { + try { + TransferData dataType= event.currentDataType; + if (isFileDataType(dataType)) { + // event.data is an array of strings which represent the + // absolute file pathes + assert event.data instanceof String[]; + dropFiles((String[]) event.data); + } else if (isMarkerDataType(dataType)) { + assert event.data instanceof IMarker[]; + dropMarkers((IMarker[]) event.data); + } else if (isTextDataType(dataType)) { + // event.data is a string + assert event.data instanceof String; + assert isDocumentEditable(); + autoScroll(SWT.NULL); + int offset= getOffsetAtLocation(event.x, event.y, true); + dropText((String) event.data, offset); + } + } catch (CoreException exc) { + ExceptionHandler.handle(exc, + CUIMessages.getString("TextEditorDropAdapter.error.title"), + CUIMessages.getString("TextEditorDropAdapter.error.message")); + } + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragEnter(org.eclipse.swt.dnd.DropTargetEvent) + */ + public void dragEnter(DropTargetEvent event) { + TransferData dataType= event.currentDataType; + if (isFileDataType(dataType)) { + // make sure the file is never moved; always do a copy + event.detail= DND.DROP_COPY; + event.feedback= DND.FEEDBACK_NONE; + } else if (isMarkerDataType(dataType)) { + event.detail= DND.DROP_COPY; + event.feedback= DND.FEEDBACK_NONE; + } else if (isTextDataType(dataType)) { + if (isDocumentEditable()) { + if (event.detail == DND.DROP_DEFAULT) { + event.detail= getAcceptableOperation(event.operations); + } + StyledText textWidget= fViewer.getTextWidget(); + textWidget.setFocus(); + fOldCaret= textWidget.getCaret(); + fCaret= new Caret(textWidget, SWT.NONE); + fCaret.setSize(fOldCaret.getSize()); + int offset= getOffsetAtLocation(event.x, event.y, true); + if (offset >= 0) { + fCaret.setLocation(textWidget.getLocationAtOffset(offset)); + } + } else { + event.detail= DND.DROP_NONE; + } + event.feedback= DND.FEEDBACK_SCROLL; + } + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragOver(org.eclipse.swt.dnd.DropTargetEvent) + */ + public void dragOver(DropTargetEvent event) { + TransferData dataType= event.currentDataType; + if (isFileDataType(dataType)) { + // make sure the file is never moved; always do a copy + event.detail= DND.DROP_COPY; + event.feedback= DND.FEEDBACK_NONE; + } else if (isMarkerDataType(dataType)) { + event.detail= DND.DROP_COPY; + event.feedback= DND.FEEDBACK_NONE; + } else if (isTextDataType(dataType)) { + if (isDocumentEditable()) { + if ((event.operations & event.detail) == 0) { + event.detail= getAcceptableOperation(event.operations); + } + int offset= getOffsetAtLocation(event.x, event.y, true); + StyledText textWidget= fViewer.getTextWidget(); + if (offset >= 0) { + fCaret.setLocation(textWidget.getLocationAtOffset(offset)); + } else { + event.detail= DND.DROP_NONE; + } + // scroll feedback has issues (bugs 149576, 139485): we do it + // ourselves + Point location= textWidget.toControl(event.x, event.y); + Rectangle viewPort= textWidget.getClientArea(); + if (location.y < textWidget.getLineHeight()) { + autoScroll(SWT.UP); + } else if (location.y > viewPort.height + - textWidget.getLineHeight()) { + autoScroll(SWT.DOWN); + } else { + autoScroll(SWT.NULL); + } + } else { + event.detail= DND.DROP_NONE; + } + event.feedback= DND.FEEDBACK_SCROLL; + } + } + + private static boolean isFileDataType(TransferData dataType) { + return FileTransfer.getInstance().isSupportedType(dataType); + } + + private static boolean isTextDataType(TransferData dataType) { + return TextTransfer.getInstance().isSupportedType(dataType); + } + + private static boolean isMarkerDataType(TransferData dataType) { + return MarkerTransfer.getInstance().isSupportedType(dataType); + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dropAccept(org.eclipse.swt.dnd.DropTargetEvent) + */ + public void dropAccept(DropTargetEvent event) { + TransferData dataType= event.currentDataType; + if (isTextDataType(dataType)) { + // check if the offset is inside the drag source selection + IDocument doc= fViewer.getDocument(); + if (doc.containsPositionCategory(TextViewerDragAdapter.DRAG_SELECTION_CATEGORY)) { + int widgetOffset= getOffsetAtLocation(event.x, event.y, true); + if (widgetOffset != -1) { + int documentOffset= getDocumentOffset(widgetOffset); + try { + Position[] dragSource= doc.getPositions(TextViewerDragAdapter.DRAG_SELECTION_CATEGORY); + if (dragSource.length == 0 + || event.detail == DND.DROP_MOVE + && dragSource[0].includes(documentOffset)) { + // do not drop-move on the drag source + event.detail = DND.DROP_NONE; + } + } catch (BadPositionCategoryException e) { + event.detail= DND.DROP_NONE; + } + } + } + } + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragOperationChanged(org.eclipse.swt.dnd.DropTargetEvent) + */ + public void dragOperationChanged(DropTargetEvent event) { + TransferData dataType= event.currentDataType; + if (isFileDataType(dataType)) { + // make sure the file is never moved; always do a copy + event.detail= DND.DROP_COPY; + event.feedback= DND.FEEDBACK_NONE; + } else if (isMarkerDataType(dataType)) { + event.detail= DND.DROP_COPY; + event.feedback= DND.FEEDBACK_NONE; + } else if (isTextDataType(dataType)) { + if (isDocumentEditable()) { + if (event.detail == DND.DROP_DEFAULT) { + event.detail= getAcceptableOperation(event.operations); + } + } else { + event.detail= DND.DROP_NONE; + } + event.feedback= DND.FEEDBACK_SCROLL; + } + } + + /** + * Get preferred operation out of allowed set. + * + * @param operations + * a bitset of allowed operations + * @return operation the preferred operation + */ + private int getAcceptableOperation(int operations) { + if ((operations & DND.DROP_MOVE) != 0 && isDocumentEditable()) { + return DND.DROP_MOVE; + } + if ((operations & DND.DROP_COPY) != 0) { + return DND.DROP_COPY; + } + return DND.DROP_NONE; + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragLeave(org.eclipse.swt.dnd.DropTargetEvent) + */ + public void dragLeave(DropTargetEvent event) { + autoScroll(SWT.NULL); + if (fOldCaret != null) { + StyledText textWidget= fViewer.getTextWidget(); + textWidget.setCaret(fOldCaret); + fOldCaret= null; + } + if (fCaret != null) { + fCaret.dispose(); + fCaret= null; + } + } + + /** + * Drop text data at offset. + * + * @param text + * @param offset + * @throws CoreException + */ + private void dropText(String text, int offset) throws CoreException { + IDocument d= fViewer.getDocument(); + try { + int docOffset= getDocumentOffset(offset); + d.replace(docOffset, 0, text); + fViewer.setSelectedRange(docOffset, text.length()); + } catch (BadLocationException e) { + // should not happen + throw new CoreException( + new Status(IStatus.ERROR, CUIPlugin.getPluginId(), 0, + e.getLocalizedMessage(), e)); + } + } + + /** + * Drop (external) files. + * + * @param fileNames + */ + private void dropFiles(String[] fileNames) throws CoreException { + for (int i= 0; i < fileNames.length; i++) { + Path path= new Path(fileNames[i]); + java.io.File file= path.toFile(); + if (!file.isFile()) { + throw new CoreException(new Status(IStatus.ERROR, CUIPlugin + .getPluginId(), 0, CUIMessages.getFormattedString( + "TextEditorDropAdapter.noFile", fileNames[i]), + null)); + } + if (file.canRead()) { + IEditorInput editorInput= EditorUtility.getEditorInputForLocation(path); + IEditorDescriptor desc= IDE.getEditorDescriptor(file.getName()); + String editorId= desc.getId(); + IDE.openEditor(getPage(), editorInput, editorId); + } else { + throw new CoreException(new Status(IStatus.ERROR, CUIPlugin + .getPluginId(), 0, CUIMessages.getFormattedString( + "TextEditorDropAdapter.unreadableFile", fileNames[i]), + null)); + } + } + } + + /** + * Drop markers (open editor and navigate to marker). + * + * @param markers + * @throws PartInitException + */ + private void dropMarkers(IMarker[] markers) throws PartInitException { + for (int i= 0; i < markers.length; i++) { + IMarker marker= markers[i]; + IDE.openEditor(getPage(), marker); + } + } + + private IWorkbenchPage getPage() { + if (fEditor != null) { + return ((IWorkbenchPart) fEditor).getSite().getPage(); + } + return CUIPlugin.getActivePage(); + } + + /** + * Convert mouse screen coordinates to a StyledText offset. + * + * @param x + * @param y + * @param absolute + * if true, coordinates are expected to be + * absolute screen coordinates + * @return text offset + * + * @see StyledText#getOffsetAtLocation() \ + */ + private int getOffsetAtLocation(int x, int y, boolean absolute) { + StyledText textWidget= fViewer.getTextWidget(); + StyledTextContent content= textWidget.getContent(); + Point location; + if (absolute) { + location= textWidget.toControl(x, y); + } else { + location= new Point(x, y); + } + int line= (textWidget.getTopPixel() + location.y) + / textWidget.getLineHeight(); + if (line >= content.getLineCount()) { + return content.getCharCount(); + } + int lineOffset= content.getOffsetAtLine(line); + String lineText= content.getLine(line); + Point endOfLine= textWidget.getLocationAtOffset(lineOffset + + lineText.length()); + if (location.x >= endOfLine.x) { + return lineOffset + lineText.length(); + } + try { + return textWidget.getOffsetAtLocation(location); + } catch (IllegalArgumentException iae) { + // we are expecting this + return -1; + } + } + + /** + * Convert a widget offset to the corresponding document offset. + * + * @param widgetOffset + * @return document offset + */ + private int getDocumentOffset(int widgetOffset) { + if (fViewer instanceof ITextViewerExtension5) { + ITextViewerExtension5 extension= (ITextViewerExtension5) fViewer; + return extension.widgetOffset2ModelOffset(widgetOffset); + } else { + IRegion visible= fViewer.getVisibleRegion(); + if (widgetOffset > visible.getLength()) { + return -1; + } + return widgetOffset + visible.getOffset(); + } + } + + /** + * Scrolls the viewer into the given direction. + * + * @param direction + * the scroll direction + */ + private void autoScroll(int direction) { + + if (fAutoScrollDirection == direction) + return; + + final int TIMER_INTERVAL= 20; + final Display display= fViewer.getTextWidget().getDisplay(); + Runnable timer= null; + switch (direction) { + case SWT.UP: + timer= new Runnable() { + public void run() { + if (fAutoScrollDirection == SWT.UP) { + int top= getInclusiveTopIndex(); + if (top > 0) { + fViewer.setTopIndex(top - 1); + display.timerExec(TIMER_INTERVAL, this); + } + } + } + }; + break; + case SWT.DOWN: + timer= new Runnable() { + public void run() { + if (fAutoScrollDirection == SWT.DOWN) { + int top= getInclusiveTopIndex(); + fViewer.setTopIndex(top + 1); + display.timerExec(TIMER_INTERVAL, this); + } + } + }; + break; + } + + fAutoScrollDirection= direction; + if (timer != null) { + display.timerExec(TIMER_INTERVAL, timer); + } + } + + /** + * Returns the viewer's first visible line, even if only partially visible. + * + * @return the viewer's first visible line + */ + private int getInclusiveTopIndex() { + StyledText textWidget= fViewer.getTextWidget(); + if (textWidget != null && !textWidget.isDisposed()) { + int top= fViewer.getTopIndex(); + if ((textWidget.getTopPixel() % textWidget.getLineHeight()) != 0) + --top; + return top; + } + return -1; + } + + /** + * @return true if the document may be changed by the drag. + */ + private boolean isDocumentEditable() { + if (fEditor instanceof ITextEditorExtension) { + return !((ITextEditorExtension) fEditor).isEditorInputReadOnly(); + } + return fViewer.isEditable(); + } + + /* + * @see org.eclipse.ui.texteditor.ITextEditorDropTargetListener#getTransfers() + */ + public Transfer[] getTransfers() { + return new Transfer[] { TextTransfer.getInstance(), + FileTransfer.getInstance(), MarkerTransfer.getInstance() }; + } + + /** + * Factory method to create a drop target listener for the given text + * editor. + * + * @param textEditor + * @return a drop target listener or null + */ + protected static ITextEditorDropTargetListener create(ITextEditor textEditor) { + ITextViewer textViewer= (ITextViewer) textEditor + .getAdapter(ITextViewer.class); + if (textViewer == null) { + // this is a little trick to get the viewer from a text editor + ITextOperationTarget target= (ITextOperationTarget) textEditor + .getAdapter(ITextOperationTarget.class); + if (target instanceof ITextViewer) { + textViewer= (ITextViewer) target; + } + } + if (textViewer == null) { + return null; + } + return new TextEditorDropAdapter(textViewer, textEditor); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dnd/TextViewerDragAdapter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dnd/TextViewerDragAdapter.java new file mode 100644 index 00000000000..55c62521bc5 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dnd/TextViewerDragAdapter.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.dnd; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.DefaultPositionUpdater; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IPositionUpdater; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.jface.text.Position; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.StyledTextContent; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSourceAdapter; +import org.eclipse.swt.dnd.DragSourceEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.ui.texteditor.ITextEditorExtension; + +/** + * Drag source adapter for text selections in ITextViewers. + */ +public class TextViewerDragAdapter extends DragSourceAdapter { + + /** The position category to be used to indicate a drag source selection */ + public final static String DRAG_SELECTION_CATEGORY= "dragSelectionCategory"; //$NON-NLS-1$ + /** The position updater for the drag selection position */ + private IPositionUpdater fPositionUpdater; + /** The drag selection position */ + private Position fSelectionPosition; + /** The text viewer allowing drag */ + private ITextViewer fViewer; + /** The editor of the viewer (may be null) */ + private ITextEditorExtension fEditor; + + /** + * Create a new TextViewerDragAdapter. + * @param viewer the text viewer + */ + public TextViewerDragAdapter(ITextViewer viewer) { + this(viewer, null); + } + + /** + * Create a new TextViewerDragAdapter. + * @param viewer the text viewer + */ + public TextViewerDragAdapter(ITextViewer viewer, ITextEditorExtension editor) { + fViewer= viewer; + fEditor= editor; + } + /* + * @see org.eclipse.swt.dnd.DragSourceListener#dragFinished(org.eclipse.swt.dnd.DragSourceEvent) + */ + public void dragFinished(DragSourceEvent event) { + IDocument doc= fViewer.getDocument(); + try { + doc.removePositionCategory(DRAG_SELECTION_CATEGORY); + doc.removePositionUpdater(fPositionUpdater); + } catch (BadPositionCategoryException e1) { + // cannot happen + } + if (event.doit && event.detail == DND.DROP_MOVE && isDocumentEditable()) { + try { + doc.replace(fSelectionPosition.offset, fSelectionPosition.length, null); + } catch (BadLocationException e) { + // ignore + } + } + if (fViewer instanceof ITextViewerExtension) { + ((ITextViewerExtension)fViewer).getRewriteTarget().endCompoundChange(); + } + } + + /* + * @see org.eclipse.swt.dnd.DragSourceListener#dragSetData(org.eclipse.swt.dnd.DragSourceEvent) + */ + public void dragSetData(DragSourceEvent event) { + IDocument doc= fViewer.getDocument(); + try { + event.data= doc.get(fSelectionPosition.offset, fSelectionPosition.length); + } catch (BadLocationException e) { + event.detail= DND.DROP_NONE; + event.doit= false; + } + } + + /* + * @see org.eclipse.swt.dnd.DragSourceListener#dragStart(org.eclipse.swt.dnd.DragSourceEvent) + */ + public void dragStart(DragSourceEvent event) { + /// convert screen coordinates to widget offest + int offset= getOffsetAtLocation(event.x, event.y, false); + // convert further to a document offset + offset= getDocumentOffset(offset); + Point selection= fViewer.getSelectedRange(); + if (selection != null && offset >= selection.x && offset < selection.x+selection.y) { + fSelectionPosition= new Position(selection.x, selection.y); + if (fViewer instanceof ITextViewerExtension) { + ((ITextViewerExtension)fViewer).getRewriteTarget().beginCompoundChange(); + } + IDocument doc= fViewer.getDocument(); + try { + // add the drag selection position + // the position is used to delete the selection on a DROP_MOVE + // and it can be used by the drop target to determine if it should + // allow the drop (e.g. if drop location overlaps selection) + doc.addPositionCategory(DRAG_SELECTION_CATEGORY); + fPositionUpdater= new DefaultPositionUpdater(DRAG_SELECTION_CATEGORY); + doc.addPositionUpdater(fPositionUpdater); + doc.addPosition(DRAG_SELECTION_CATEGORY, fSelectionPosition); + } catch (BadLocationException e) { + // should not happen + } catch (BadPositionCategoryException e) { + // cannot happen + } + event.doit= true; + // this has no effect? + event.detail = DND.DROP_COPY; + if (isDocumentEditable()) { + event.detail |= DND.DROP_MOVE; + } + } else { + event.doit= false; + event.detail = DND.DROP_NONE; + } + } + + /** + * Convert mouse screen coordinates to a StyledText offset. + * @param x + * @param y + * @param absolute if true, coordinates are expected to be absolute + * screen coordinates + * @return text offset + */ + private int getOffsetAtLocation(int x, int y, boolean absolute) { + StyledText textWidget= fViewer.getTextWidget(); + StyledTextContent content= textWidget.getContent(); + Point location; + if (absolute) { + location= textWidget.toControl(x, y); + } else { + location= new Point(x ,y); + } + int line= (textWidget.getTopPixel() + location.y) / textWidget.getLineHeight(); + if (line >= content.getLineCount()) { + return content.getCharCount(); + } + int lineOffset= content.getOffsetAtLine(line); + String lineText= content.getLine(line); + Point endOfLine= textWidget.getLocationAtOffset(lineOffset + lineText.length()); + if (location.x >= endOfLine.x) { + return lineOffset + lineText.length(); + } + try { + return textWidget.getOffsetAtLocation(location); + } catch (IllegalArgumentException iae) { + // we are expecting this + return -1; + } + } + + /** + * Convert a widget offset to the corresponding document offset. + * @param widgetOffset + * @return document offset + */ + private int getDocumentOffset(int widgetOffset) { + if (fViewer instanceof ITextViewerExtension5) { + ITextViewerExtension5 extension= (ITextViewerExtension5)fViewer; + return extension.widgetOffset2ModelOffset(widgetOffset); + } else { + IRegion visible= fViewer.getVisibleRegion(); + if (widgetOffset > visible.getLength()) { + return -1; + } + return widgetOffset + visible.getOffset(); + } + } + + /** + * @return true if the document may be changed by the drag. + */ + private boolean isDocumentEditable() { + if (fEditor != null) { + return !fEditor.isEditorInputReadOnly(); + } + return fViewer.isEditable(); + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index c935cec4d39..912759edf2b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -32,6 +32,8 @@ import org.eclipse.cdt.internal.ui.actions.FoldingActionGroup; import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; import org.eclipse.cdt.internal.ui.actions.JoinLinesAction; import org.eclipse.cdt.internal.ui.actions.RemoveBlockCommentAction; +import org.eclipse.cdt.internal.ui.dnd.TextEditorDropAdapter; +import org.eclipse.cdt.internal.ui.dnd.TextViewerDragAdapter; import org.eclipse.cdt.internal.ui.search.actions.OpenDeclarationsAction; import org.eclipse.cdt.internal.ui.search.actions.OpenDefinitionAction; import org.eclipse.cdt.internal.ui.search.actions.SelectionSearchGroup; @@ -87,9 +89,16 @@ import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.search.ui.actions.TextSearchGroup; import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSource; +import org.eclipse.swt.dnd.DragSourceListener; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IFileEditorInput; @@ -113,6 +122,7 @@ import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; +import org.eclipse.ui.texteditor.ITextEditorDropTargetListener; import org.eclipse.ui.texteditor.MarkerAnnotation; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.texteditor.TextOperationAction; @@ -839,6 +849,25 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS } + /* + * @see org.eclipse.ui.texteditor.AbstractTextEditor#initializeDragAndDrop(org.eclipse.jface.text.source.ISourceViewer) + */ + protected void initializeDragAndDrop(ISourceViewer viewer) { + Control control= viewer.getTextWidget(); + int operations= DND.DROP_MOVE | DND.DROP_COPY; + + DropTarget dropTarget= new DropTarget(control, operations); + ITextEditorDropTargetListener dropTargetListener= new TextEditorDropAdapter(viewer, this); + dropTarget.setTransfer(dropTargetListener.getTransfers()); + dropTarget.addDropListener(dropTargetListener); + + DragSource dragSource= new DragSource(control, operations); + Transfer[] dragTypes= new Transfer[] { TextTransfer.getInstance() }; + dragSource.setTransfer(dragTypes); + DragSourceListener dragSourceListener= new TextViewerDragAdapter(viewer, this); + dragSource.addDragListener(dragSourceListener); + } + /* (non-Javadoc) * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#gotoMarker(org.eclipse.core.resources.IMarker) */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java index b85a61e962b..c2079ae8a2f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java @@ -29,6 +29,8 @@ import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.editor.CEditorMessages; import org.eclipse.cdt.internal.ui.editor.ITranslationUnitEditorInput; import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -261,6 +263,24 @@ public class EditorUtility { return null; } + /** + * Utility method to get an editor input for the given file system location. + * If the location denotes a workspace file, a FileEditorInput + * is returned, otherwise, the input is an IStorageEditorInput + * assuming the location points to an existing file in the file system. + * + * @param location a valid file system location + * @return an editor input + */ + public static IEditorInput getEditorInputForLocation(IPath location) { + IFile resource= FileBuffers.getWorkspaceFileAtLocation(location); + if (resource != null) { + return new FileEditorInput(resource); + } else { + return new ExternalEditorInput(new FileStorage(location)); + } + } + /** * If the current active editor edits a c element return it, else @@ -282,7 +302,7 @@ public class EditorUtility { /** * Gets the working copy of an compilation unit opened in an editor - * @param part the editor part + * * @param cu the original compilation unit (or another working copy) * @return the working copy of the compilation unit, or null if not found */