blob: f61712fb0352d060da056faab536d513a7318e8d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.
*
* @since 4.0
*/
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;
/** Flag whether this drag source listener allows to drag */
private boolean fIsEnabled = true;
/**
* 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)
*/
@Override
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)
*/
@Override
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)
*/
@Override
public void dragStart(DragSourceEvent event) {
if (!fIsEnabled) {
event.doit = false;
return;
}
// 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 <code>StyledText</code> offset.
*
* @param x
* screen X-coordinate
* @param y
* screen Y-coordinate
* @param absolute
* if <code>true</code>, 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);
}
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();
}
/**
* Enable or disable this drag listener.
* @param enabled
*/
public void setEnabled(boolean enabled) {
fIsEnabled = enabled;
}
}