How can I retain the selected text when trying to drag/drop text from a JTextField?
Created Sep 28, 2001
Scott Stanchfield
The caret considers "press" a change of location. The drag gesture waits for a drag. Of course, this means that any press unselects the text.
This is a nasty bug in Swing...
The problem is that the Caret's mouse listener and the DragGesture's mouse listener are watching for the same thing.
The caret considers "press" a change of location. The drag gesture waits for a drag. Of course, this means that any press unselects the text.
The following code will fix the problem.
DragDropCaret - replacement Caret that cooperates with a dragger
import java.awt.Component;
import java.awt.Point;
import java.awt.dnd.DragGestureEvent;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.text.DefaultCaret;
public class DragDropCaret extends DefaultCaret {
private boolean isDragging = false;
public void mouseClicked(MouseEvent e) {
if ((getComponent() != null) && getComponent().isEnabled()) {
getComponent().requestFocus();
}
super.mouseClicked(e);
}
public void mouseDragged(MouseEvent e) {
if (isDragging) {
if ((getComponent() != null) && getComponent().isEnabled()) {
getComponent().requestFocus();
}
moveCaret(e);
}
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {
if (isDragging)
isDragging = false;
else
positionCaret(e);
}
public void startDrag(DragGestureEvent e) {
positionCaret((MouseEvent)e.getTriggerEvent());
isDragging = true;
}
}
TextDragDropper - Helper class to set up drag/drop for a text field. Needs some work to handle non-text drags...
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
public class TextDragDropper implements DragGestureListener, DragSourceListener,
DropTargetListener {
private DragSource dragSource;
private DropTarget dropTarget;
private DragGestureRecognizer recognizer;
private DragDropCaret caret;
private JTextComponent textComponent;
private boolean isLeftMB = false;
private int start = 0;
private int end = 0;
private boolean isCopy = false;
public TextDragDropper(JTextComponent textComponent) {
this.textComponent = textComponent;
this.textComponent.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
isLeftMB = SwingUtilities.isLeftMouseButton(e);
}
});
dragSource = new DragSource();
dropTarget = new DropTarget(textComponent, DnDConstants.ACTION_COPY_OR_MOVE, this);
recognizer = dragSource.createDefaultDragGestureRecognizer (
textComponent, DnDConstants.ACTION_COPY_OR_MOVE, this );
caret = new DragDropCaret();
textComponent.setCaret(caret);
}
public void dragDropEnd(DragSourceDropEvent dsde) {
if (dsde.getDropSuccess()) {
if (start != end) {
textComponent.setCaretPosition(start);
textComponent.moveCaretPosition(end);
}
if (!isCopy)
textComponent.replaceSelection("");
}
}
public void dragEnter(DragSourceDragEvent dsde) {}
public void dragEnter(DropTargetDragEvent dtde) {
if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor))
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
else
dtde.rejectDrag();
}
public void dragExit(DragSourceEvent dse) {}
public void dragExit(DropTargetEvent dte) {}
public void dragGestureRecognized(DragGestureEvent dge) {
if (!isLeftMB)
return;
int n = textComponent.viewToModel(dge.getDragOrigin());
if (n > textComponent.getSelectionStart() && n <= textComponent.getSelectionEnd()) {
String selectedItem = textComponent.getSelectedText();
MouseEvent e = (MouseEvent)dge.getTriggerEvent();
// NOTE: SPECIFIC TO WINDOWS DRAG... CREATES "LINK"
// IGNORE IT...
// IS THIS OK TO IGNORE FOR OTHER PLATFORMS? NEED TO CHECK
if (e.isAltDown())
return;
isCopy = e.isControlDown();
dge.startDrag((isCopy ? DragSource.DefaultCopyDrop : DragSource.DefaultMoveDrop), new StringSelection(selectedItem), this);
}
else {
caret.startDrag(dge);
}
}
public void dragOver(DragSourceDragEvent dsde) { }
public void dragOver(DropTargetDragEvent dtde) { }
public void drop(DropTargetDropEvent dtde) {
start = textComponent.getSelectionStart();
end = textComponent.getSelectionEnd();
try {
if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
Transferable tr = dtde.getTransferable();
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
String s = (String)tr.getTransferData(DataFlavor.stringFlavor);
int n = textComponent.viewToModel(dtde.getLocation());
Document doc = textComponent.getDocument();
doc.insertString(n, s, null);
dtde.dropComplete(true);
}
else {
dtde.rejectDrop();
}
}
catch(IOException e) {
dtde.rejectDrop();
}
catch(UnsupportedFlavorException e) {
dtde.rejectDrop();
}
catch(BadLocationException e) {
dtde.rejectDrop();
}
catch(Exception e) {
e.printStackTrace();
}
}
public void dropActionChanged(DragSourceDragEvent dsde) { }
public void dropActionChanged(DropTargetDragEvent dtde) { }
/**
* Gets the textComponent
* @return Returns a JTextComponent
*/
protected JTextComponent getTextComponent() {
return textComponent;
}
/**
* Sets the textComponent
* @param textComponent The textComponent to set
*/
protected void setTextComponent(JTextComponent textComponent) {
this.textComponent = textComponent;
}
/**
* Gets the start
* @return Returns a int
*/
protected int getStart() {
return start;
}
/**
* Sets the start
* @param start The start to set
*/
protected void setStart(int start) {
this.start = start;
}
/**
* Gets the recognizer
* @return Returns a DragGestureRecognizer
*/
protected DragGestureRecognizer getRecognizer() {
return recognizer;
}
/**
* Sets the recognizer
* @param recognizer The recognizer to set
*/
protected void setRecognizer(DragGestureRecognizer recognizer) {
this.recognizer = recognizer;
}
/**
* Gets the isLeftMB
* @return Returns a boolean
*/
protected boolean getIsLeftMB() {
return isLeftMB;
}
/**
* Sets the isLeftMB
* @param isLeftMB The isLeftMB to set
*/
protected void setIsLeftMB(boolean isLeftMB) {
this.isLeftMB = isLeftMB;
}
/**
* Gets the isCopy
* @return Returns a boolean
*/
protected boolean getIsCopy() {
return isCopy;
}
/**
* Sets the isCopy
* @param isCopy The isCopy to set
*/
protected void setIsCopy(boolean isCopy) {
this.isCopy = isCopy;
}
/**
* Gets the end
* @return Returns a int
*/
protected int getEnd() {
return end;
}
/**
* Sets the end
* @param end The end to set
*/
protected void setEnd(int end) {
this.end = end;
}
/**
* Gets the dropTarget
* @return Returns a DropTarget
*/
protected DropTarget getDropTarget() {
return dropTarget;
}
/**
* Sets the dropTarget
* @param dropTarget The dropTarget to set
*/
protected void setDropTarget(DropTarget dropTarget) {
this.dropTarget = dropTarget;
}
/**
* Gets the dragSource
* @return Returns a DragSource
*/
protected DragSource getDragSource() {
return dragSource;
}
/**
* Sets the dragSource
* @param dragSource The dragSource to set
*/
protected void setDragSource(DragSource dragSource) {
this.dragSource = dragSource;
}
/**
* Gets the caret
* @return Returns a DragDropCaret
*/
protected DragDropCaret getCaret() {
return caret;
}
/**
* Sets the caret
* @param caret The caret to set
*/
protected void setCaret(DragDropCaret caret) {
this.caret = caret;
}
}
Finally, create a subclass of JTextField and add
new TextDragDropper(this);
to its constructors