Skip to content

DragSource: make a new drag context for each drag - #69

Open
duncanmcqueen wants to merge 1 commit into
ingelabs:masterfrom
duncanmcqueen:fix-dragsource-context
Open

duncanmcqueen wants to merge 1 commit into
ingelabs:masterfrom
duncanmcqueen:fix-dragsource-context

Conversation

@duncanmcqueen

Copy link
Copy Markdown
Contributor

Split from #57.

Problem and fix

DragSource.startDrag() makes the drag source context peer and the DragSourceContext only when its fields are null. Thus the second and all later drags use the peer and the context of the first drag, with the trigger event and the Transferable of the first drag. The user drags the data of the first drag again.

Now startDrag() makes a new peer and a new context for each drag. I found this bug when I wrote an AWT toolkit for Haiku, which supplies drag and drop peers.

How to reproduce

The GTK peer starts a native drag, so the test uses a toolkit that replaces only createDragSourceContextPeer(). Its peer records the Transferable of each drag. The toolkit class must be on the boot class path, because Toolkit loads it with the boot class loader. The test needs a display, because the toolkit extends GtkToolkit.

javac DragSourceNewContext.java
java -Xbootclasspath/a:. -Dawt.toolkit='DragSourceNewContext$TestToolkit' DragSourceNewContext
DragSourceNewContext.java
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Each DragSource.startDrag() must start a drag with its own
 * Transferable. A toolkit that records each drag replaces the native
 * drag; the toolkit class must be on the boot class path:
 *
 *   java -Xbootclasspath/a:. -Dawt.toolkit=DragSourceNewContext\$TestToolkit DragSourceNewContext
 */
public class DragSourceNewContext
{
  static List<Transferable> started = new ArrayList<Transferable>();

  public static class TestToolkit extends gnu.java.awt.peer.gtk.GtkToolkit
  {
    public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e)
    {
      return new DragSourceContextPeer()
        {
          public void startDrag(DragSourceContext c, Cursor cur, Image i,
                                Point p)
          {
            started.add(c.getTransferable());
          }
          public Cursor getCursor() { return null; }
          public void setCursor(Cursor c) {}
          public void transferablesFlavorsChanged() {}
        };
    }
  }

  public static void main(String[] args) throws Exception
  {
    Canvas c = new Canvas();
    DragSource ds = new DragSource();
    DragGestureRecognizer r = new DragGestureRecognizer(ds, c,
                                                        DnDConstants.ACTION_COPY)
      {
        protected void registerListeners() {}
        protected void unregisterListeners() {}
      };
    MouseEvent me = new MouseEvent(c, MouseEvent.MOUSE_DRAGGED, 0, 0, 5, 5, 0,
                                   false);
    DragGestureEvent e = new DragGestureEvent(r, DnDConstants.ACTION_COPY,
                                              new Point(5, 5),
                                              Collections.singletonList(me));
    ds.startDrag(e, null, new StringSelection("first"), null);
    ds.startDrag(e, null, new StringSelection("second"), null);
    String second = (String) started.get(1)
                    .getTransferData(DataFlavor.stringFlavor);
    boolean ok = "second".equals(second);
    System.out.println((ok ? "PASS" : "FAIL") + ": the second drag has the data \""
                       + second + "\", expected \"second\"");
    System.exit(ok ? 0 : 1);
  }
}

Results

Linux x86_64, ingelabs/classpath master (0406074), JamVM master, javac from JDK 8, autoconf 2.73, automake 1.19, the configure options of the CI Linux job, out-of-tree build. "Before" is master. "After" is master with this commit.

Before:
FAIL: the second drag has the data "first", expected "second"

After:
PASS: the second drag has the data "second", expected "second"

Platforms

Not specific to Haiku. The problem is in java.awt.dnd.DragSource and occurs with all toolkits. I reproduced it on Linux.

AI assistance

An AI model (Claude Code, by Anthropic) wrote this change, the test and this text. The repository owner directed the work and checked the results. Please review it as you review other contributions.

🤖 Generated with Claude Code

startDrag() made the drag source context peer and the context only
when its fields were null, so every later drag reused the context of
the first drag, with its trigger event and its Transferable. Make a
new peer and a new context for each drag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: duncanmcqueen <206344294+duncanmcqueen@users.noreply.github.com>
@duncanmcqueen
duncanmcqueen marked this pull request as ready for review October 14, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant