rewrote EditorGutterComponent DnD functionality to fix IDEA-36592 Breakpoint drag and drop does not work between two splits (no matter the same or different files)

This commit is contained in:
Konstantin Bulenkov
2011-02-03 19:26:37 +03:00
parent 853f00e91c
commit ac0d984e8d
4 changed files with 88 additions and 145 deletions
@@ -43,6 +43,7 @@ import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.jsp.JspFile;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.util.StringBuilderSpinAllocator;
@@ -444,8 +445,10 @@ public abstract class BreakpointWithHighlighter extends Breakpoint {
return false;
}
final PsiFile psiFile = position.getFile();
final PsiFile oldFile = getSourcePosition().getFile();
final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(psiFile);
if (document == null) {
final Document oldDocument = PsiDocumentManager.getInstance(getProject()).getDocument(oldFile);
if (document == null || oldDocument == null) {
return false;
}
final RangeHighlighter newHighlighter = createHighlighter(myProject, document, position.getLine());
@@ -458,13 +461,13 @@ public abstract class BreakpointWithHighlighter extends Breakpoint {
reload();
if(!isValid()) {
document.getMarkupModel(myProject).removeHighlighter(myHighlighter);
oldDocument.getMarkupModel(myProject).removeHighlighter(myHighlighter);
myHighlighter = oldHighlighter;
reload();
return false;
}
document.getMarkupModel(myProject).removeHighlighter(oldHighlighter);
oldDocument.getMarkupModel(myProject).removeHighlighter(oldHighlighter);
DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager().fireBreakpointChanged(this);
updateUI();
@@ -666,11 +669,9 @@ public abstract class BreakpointWithHighlighter extends Breakpoint {
public GutterDraggableObject getDraggableObject() {
return new GutterDraggableObject() {
public void removeSelf() {
}
public boolean copy(int line) {
return moveTo(SourcePosition.createFromLine(getSourcePosition().getFile(), line));
public boolean copy(int line, VirtualFile file) {
final PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(file);
return psiFile == null ? false : moveTo(SourcePosition.createFromLine(psiFile, line));
}
public Cursor getCursor(int line) {
@@ -15,6 +15,8 @@
*/
package com.intellij.openapi.editor.markup;
import com.intellij.openapi.vfs.VirtualFile;
import java.awt.*;
/**
@@ -22,21 +24,20 @@ import java.awt.*;
* a gutter icon which can be dragged and dropped is the breakpoint icon.
*
* @author ven
* @author Konstantin Bulenkov
* @see GutterIconRenderer#getDraggableObject()
*/
public interface GutterDraggableObject {
/**
* Called when the drag and drop operation is successfully completed.
*/
void removeSelf();
/**
* Called when the icon is dropped over the specified line.
*
*
* @param line the line over which the icon has been dropped.
* @param file the DnD target file
* @return true if the drag and drop operation has completed successfully, false otherwise.
* @since 10.0.3
*/
boolean copy(int line);
boolean copy(int line, VirtualFile file);
/**
* Returns the cursor to show when the drag is over the specified line.
@@ -27,6 +27,7 @@ package com.intellij.openapi.editor.impl;
import com.intellij.codeInsight.hint.TooltipController;
import com.intellij.codeInsight.hint.TooltipGroup;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.dnd.*;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.customization.CustomActionsSchema;
import com.intellij.openapi.actionSystem.*;
@@ -43,10 +44,12 @@ import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.HintHint;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.IconUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntArrayList;
@@ -59,15 +62,12 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.util.*;
import java.util.List;
class EditorGutterComponentImpl extends EditorGutterComponentEx implements MouseListener, MouseMotionListener {
class EditorGutterComponentImpl extends EditorGutterComponentEx implements MouseListener, MouseMotionListener, DnDTarget, DnDSource {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorGutterComponentImpl");
private static final int START_ICON_AREA_WIDTH = 15;
private static final int FREE_PAINTERS_AREA_WIDTH = 3;
@@ -86,16 +86,14 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
private final Map<TextAnnotationGutterProvider, EditorGutterAction> myProviderToListener = new HashMap<TextAnnotationGutterProvider, EditorGutterAction>();
private static final int GAP_BETWEEN_ANNOTATIONS = 6;
private Color myBackgroundColor = null;
private GutterDraggableObject myGutterDraggableObject;
private String myLastGutterToolTip = null;
private int myLastPreferredHeight = -1;
public EditorGutterComponentImpl(EditorImpl editor) {
myEditor = editor;
if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
new DropTarget(this, new MyDropTargetListener());
final DragSource dragSource = DragSource.getDefaultDragSource();
dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, new MyDragGestureListener());
DnDManager.getInstance().registerSource(this, this);
DnDManager.getInstance().registerTarget(this, this);
}
setOpaque(true);
}
@@ -342,6 +340,57 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
g2.setTransform(old);
}
@Override
public boolean update(DnDEvent aEvent) {
aEvent.setDropPossible(true, null);
return false;
}
@Override
public void drop(DnDEvent aEvent) {
final Object attachedObject = aEvent.getAttachedObject();
if (attachedObject instanceof GutterIconRenderer) {
final GutterDraggableObject draggableObject = ((GutterIconRenderer)attachedObject).getDraggableObject();
if (draggableObject != null) {
final int line = convertPointToLineNumber(aEvent.getPoint());
if (line != -1) {
draggableObject.copy(line, myEditor.getVirtualFile());
}
}
}
}
@Override
public void cleanUpOnLeave() {
}
@Override
public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) {
}
@Override
public boolean canStartDragging(DnDAction action, Point dragOrigin) {
return (action == DnDAction.MOVE || action == DnDAction.COPY) && getGutterRenderer(dragOrigin) != null;
}
@Override
public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) {
return new DnDDragStartBean(getGutterRenderer(dragOrigin));
}
@Override
public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) {
return new Pair<Image, Point>(IconUtil.toImage(getGutterRenderer(dragOrigin).getIcon()), dragOrigin);
}
@Override
public void dragDropEnd() {
}
@Override
public void dropActionChanged(int gestureModifiers) {
}
private interface RangeHighlighterProcessor {
void process(RangeHighlighter highlighter);
}
@@ -1264,25 +1313,30 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
TooltipController.getInstance().cancelTooltip(GUTTER_TOOLTIP_GROUP, e, false);
}
@Nullable
private GutterIconRenderer getGutterRenderer(final Point p) {
final int ex = convertX((int)p.getX());
private int convertPointToLineNumber(final Point p) {
int line = myEditor.yPositionToLogicalLineNumber((int)p.getY());
if (line >= myEditor.getDocument().getLineCount()) return null;
if (line >= myEditor.getDocument().getLineCount()) return -1;
int startOffset = myEditor.getDocument().getLineStartOffset(line);
final FoldRegion region = myEditor.getFoldingModel().getCollapsedRegionAtOffset(startOffset);
if (region != null) {
line = myEditor.getDocument().getLineNumber(region.getEndOffset());
if (line >= myEditor.getDocument().getLineCount()) return null;
if (line >= myEditor.getDocument().getLineCount()) return -1;
}
return line;
}
@Nullable
private GutterIconRenderer getGutterRenderer(final Point p) {
int line = convertPointToLineNumber(p);
if (line == -1) return null;
ArrayList<GutterIconRenderer> renderers = myLineToGutterRenderers.get(line);
if (renderers == null) return null;
final GutterIconRenderer[] result = {null};
processIconsRow(line, renderers, new LineGutterIconRendererProcessor() {
public void process(int x, int y, GutterIconRenderer renderer) {
final int ex = convertX((int)p.getX());
Icon icon = renderer.getIcon();
if (x <= ex && ex <= x + icon.getIconWidth() &&
y <= p.getY() && p.getY() <= y + icon.getIconHeight()) {
@@ -1310,114 +1364,4 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
}
myProviderToListener.clear();
}
private static final DataFlavor[] FLAVORS;
static {
DataFlavor[] flavors;
try {
final Class<EditorGutterComponentImpl> aClass = EditorGutterComponentImpl.class;
//noinspection HardCodedStringLiteral
flavors = new DataFlavor[]{new DataFlavor(
DataFlavor.javaJVMLocalObjectMimeType + ";class=" + aClass.getName(), "GutterTransferable", aClass.getClassLoader()
)};
}
catch (ClassNotFoundException e) {
LOG.error(e); // should not happen
flavors = new DataFlavor[0];
}
FLAVORS = flavors;
}
private class MyDragGestureListener implements DragGestureListener {
public void dragGestureRecognized(DragGestureEvent dge) {
if ((dge.getDragAction() & DnDConstants.ACTION_MOVE) == 0) return;
final GutterIconRenderer renderer = getGutterRenderer(dge.getDragOrigin());
if (renderer != null) {
final GutterDraggableObject draggableObject = renderer.getDraggableObject();
if (draggableObject != null) {
try {
myGutterDraggableObject = draggableObject;
final MyDragSourceListener dragSourceListener = new MyDragSourceListener();
dge.startDrag(DragSource.DefaultMoveNoDrop, new Transferable () {
public DataFlavor[] getTransferDataFlavors() {
return FLAVORS;
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
DataFlavor[] flavors = getTransferDataFlavors();
for (DataFlavor flavor1 : flavors) {
if (flavor.equals(flavor1)) {
return true;
}
}
return false;
}
public Object getTransferData(DataFlavor flavor) {
return null;
}
}, dragSourceListener);
}
catch (InvalidDnDOperationException e) {
// OK, can't dnd
}
}
}
}
}
private class MyDragSourceListener extends DragSourceAdapter {
public void dragEnter(DragSourceDragEvent e) {
updateCursor(e);
}
public void dragOver(DragSourceDragEvent e) {
updateCursor(e);
}
public void dropActionChanged(DragSourceDragEvent e) {
e.getDragSourceContext().setCursor(null);//setCursor (e.getDragSourceContext());
}
private void updateCursor(final DragSourceDragEvent e) {
final DragSourceContext context = e.getDragSourceContext();
final Point screenPoint = e.getLocation();
if (screenPoint != null) {
final Point gutterPoint = new Point(screenPoint);
SwingUtilities.convertPointFromScreen(gutterPoint, EditorGutterComponentImpl.this);
if (contains(gutterPoint)){
final Point editorPoint = new Point(screenPoint);
SwingUtilities.convertPointFromScreen(editorPoint, myEditor.getContentComponent());
int line = myEditor.yPositionToLogicalLineNumber((int)editorPoint.getY());
final Cursor cursor = myGutterDraggableObject.getCursor(line);
context.setCursor(cursor);
return;
}
}
context.setCursor(null);
}
public void dragDropEnd(DragSourceDropEvent e) {
if (!e.getDropSuccess()) return;
if (e.getDropAction() == DnDConstants.ACTION_MOVE) {
myGutterDraggableObject.removeSelf();
}
}
}
private class MyDropTargetListener extends DropTargetAdapter {
public void drop(DropTargetDropEvent e) {
if (myGutterDraggableObject != null) {
int dropAction = e.getDropAction();
if ((dropAction & DnDConstants.ACTION_MOVE) != 0) {
int line = myEditor.yPositionToLogicalLineNumber((int)e.getLocation().getY());
e.dropComplete(myGutterDraggableObject.copy(line));
return;
}
}
e.rejectDrop();
}
}
}
@@ -270,8 +270,7 @@ public class XLineBreakpointImpl<P extends XBreakpointProperties> extends XBreak
}
}
private boolean canMoveTo(int line) {
final VirtualFile file = getFile();
private boolean canMoveTo(int line, VirtualFile file) {
return file != null && myType.canPutAt(file, line, getProject());
}
@@ -389,11 +388,9 @@ public class XLineBreakpointImpl<P extends XBreakpointProperties> extends XBreak
@Override
public GutterDraggableObject getDraggableObject() {
return new GutterDraggableObject() {
public void removeSelf() {
}
public boolean copy(int line) {
if (canMoveTo(line)) {
public boolean copy(int line, VirtualFile file) {
if (canMoveTo(line, file)) {
setFileUrl(file.getUrl());
setLine(line);
return true;
}
@@ -401,7 +398,7 @@ public class XLineBreakpointImpl<P extends XBreakpointProperties> extends XBreak
}
public Cursor getCursor(int line) {
return canMoveTo(line) ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop;
return canMoveTo(line, getFile()) ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop;
}
};
}