How can I retain the selected text when trying to drag/drop text from a JTextField?

Scott Stanchfield

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

Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

About | Sitemap | Contact