--- old/src/macosx/classes/sun/lwawt/macosx/CDropTargetContextPeer.java 2013-09-25 15:03:46.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/macosx/CDropTargetContextPeer.java 2013-09-25 15:03:46.000000000 +0400 @@ -26,6 +26,7 @@ package sun.lwawt.macosx; import java.awt.*; +import java.awt.dnd.DropTarget; import sun.awt.dnd.SunDropTargetContextPeer; import sun.awt.dnd.SunDropTargetEvent; @@ -38,7 +39,7 @@ private long fNativeDropTransfer = 0; private long fNativeDataAvailable = 0; private Object fNativeData = null; - private boolean insideTarget = true; + private DropTarget insideTarget = null; Object awtLockAccess = new Object(); @@ -88,26 +89,19 @@ return fNativeData; } - // We need to take care of dragExit message because for some reason it is not being - // generated for lightweight components + // We need to take care of dragEnter and dragExit messages because + // native system generates them only for heavyweights @Override protected void processMotionMessage(SunDropTargetEvent event, boolean operationChanged) { - Component eventSource = (Component)event.getComponent(); - Point screenPoint = event.getPoint(); - SwingUtilities.convertPointToScreen(screenPoint, eventSource); - Rectangle screenBounds = new Rectangle(eventSource.getLocationOnScreen().x, - eventSource.getLocationOnScreen().y, - eventSource.getWidth(), eventSource.getHeight()); - if(insideTarget) { - if(!screenBounds.contains(screenPoint)) { + boolean eventInsideTarget = isEventInsideTarget(event); + if(event.getComponent().getDropTarget() == insideTarget) { + if(!eventInsideTarget) { processExitMessage(event); - insideTarget = false; return; } } else { - if(screenBounds.contains(screenPoint)) { + if(eventInsideTarget) { processEnterMessage(event); - insideTarget = true; } else { return; } @@ -115,17 +109,50 @@ super.processMotionMessage(event, operationChanged); } + /** + * Could be called when DnD enters a heavyweight or synthesized in processMotionMessage + */ + @Override + protected void processEnterMessage(SunDropTargetEvent event) { + Component c = event.getComponent(); + DropTarget dt = event.getComponent().getDropTarget(); + if (isEventInsideTarget(event) + && dt != insideTarget + && c.isShowing() + && dt != null + && dt.isActive()) { + insideTarget = dt; + super.processEnterMessage(event); + } + } + + /** + * Could be called when DnD exits a heavyweight or synthesized in processMotionMessage + */ + @Override + protected void processExitMessage(SunDropTargetEvent event) { + if(event.getComponent().getDropTarget() == insideTarget) { + insideTarget = null; + super.processExitMessage(event); + } + } + @Override protected void processDropMessage(SunDropTargetEvent event) { - Component eventSource = (Component)event.getComponent(); + if(isEventInsideTarget(event)) { + super.processDropMessage(event); + insideTarget = null; + } + } + + private boolean isEventInsideTarget(SunDropTargetEvent event) { + Component eventSource = event.getComponent(); Point screenPoint = event.getPoint(); SwingUtilities.convertPointToScreen(screenPoint, eventSource); Rectangle screenBounds = new Rectangle(eventSource.getLocationOnScreen().x, eventSource.getLocationOnScreen().y, eventSource.getWidth(), eventSource.getHeight()); - if(screenBounds.contains(screenPoint)) { - super.processDropMessage(event); - } + return screenBounds.contains(screenPoint); } @Override --- old/src/macosx/native/sun/awt/CDropTarget.m 2013-09-25 15:03:47.000000000 +0400 +++ new/src/macosx/native/sun/awt/CDropTarget.m 2013-09-25 15:03:47.000000000 +0400 @@ -477,6 +477,8 @@ sDraggingExited = FALSE; sDraggingLocation = [sender draggingLocation]; NSPoint javaLocation = [fView convertPoint:sDraggingLocation fromView:nil]; + javaLocation.y = fView.window.frame.size.height - javaLocation.y; + DLog5(@"+ dragEnter: loc native %f, %f, java %f, %f\n", sDraggingLocation.x, sDraggingLocation.y, javaLocation.x, javaLocation.y); ////////// BEGIN Calculate the current drag actions ////////// @@ -570,8 +572,7 @@ // Should we notify Java things have changed? if (sDraggingError == FALSE && notifyJava) { NSPoint javaLocation = [fView convertPoint:sDraggingLocation fromView:nil]; - // For some reason even after the convertPoint drag events come with the y coordinate reverted - javaLocation.y = fView.window.frame.size.height - javaLocation.y; + javaLocation.y = fView.window.frame.size.height - javaLocation.y; //DLog5(@" : dragMoved: loc native %f, %f, java %f, %f\n", sDraggingLocation.x, sDraggingLocation.y, javaLocation.x, javaLocation.y); jlongArray formats = sDraggingFormats; --- /dev/null 2013-09-25 15:03:48.000000000 +0400 +++ new/test/java/awt/dnd/DropTargetEnterExitTest/ExtraDragEnterTest.java 2013-09-25 15:03:47.000000000 +0400 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8024163 + * @summary Checks the dragEnter event is correctly generated + * @library ../../regtesthelpers + * @build Util + * @compile ExtraDragEnterTest.java + * @run main/othervm ExtraDragEnterTest + * @author Petr Pchelko + */ + +import test.java.awt.regtesthelpers.Util; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.InputEvent; +import java.util.concurrent.atomic.AtomicInteger; + +public class ExtraDragEnterTest { + + private static final int FRAME_SIZE = 100; + private static final int FRAME_LOCATION = 100; + + private static AtomicInteger dragEnterCalled = new AtomicInteger(0); + + private static volatile Panel mainPanel; + private static volatile Frame f; + + private static void initAndShowUI() { + f = new Frame("Test frame"); + f.setBounds(FRAME_LOCATION,FRAME_LOCATION,FRAME_SIZE,FRAME_SIZE); + mainPanel = new Panel(); + mainPanel.setBounds(0, 0, FRAME_SIZE, FRAME_SIZE); + mainPanel.setBackground(Color.black); + mainPanel.setLayout(new GridLayout(2, 1)); + + final DraggablePanel dragSource = new DraggablePanel(); + dragSource.setBackground(Color.yellow); + dragSource.setDropTarget(null); + mainPanel.add(dragSource); + + Panel dropTarget = new Panel(); + dropTarget.setBackground(Color.red); + DropTarget dt = new DropTarget(dropTarget, new DropTargetAdapter() { + @Override public void drop(DropTargetDropEvent dtde) { } + + @Override + public void dragEnter(DropTargetDragEvent dtde) { + dragEnterCalled.incrementAndGet(); + } + }); + dropTarget.setDropTarget(dt); + mainPanel.add(dropTarget); + + f.add(mainPanel); + f.setVisible(true); + } + + public static void main(String[] args) throws Throwable { + try { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + initAndShowUI(); + } + }); + + Robot r = new Robot(); + Util.waitForIdle(r); + Point leftCorner = new Point(mainPanel.getLocationOnScreen()); + leftCorner.translate(5, 5); + Point rightCorner = new Point(mainPanel.getLocationOnScreen()); + rightCorner.translate(mainPanel.getWidth(), mainPanel.getHeight()); + rightCorner.translate(-5, -5); + Util.drag(r, leftCorner, rightCorner, InputEvent.BUTTON1_MASK); + Util.waitForIdle(r); + + int called = dragEnterCalled.get(); + if (called != 1) { + throw new RuntimeException("Failed. Drag enter called " + called + " times. Expected 1" ); + } + } finally { + if (f != null) { + f.dispose(); + } + } + } + + private static class DraggablePanel extends Panel implements DragGestureListener { + + public DraggablePanel() { + (new DragSource()).createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this); + } + + @Override + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(Cursor.getDefaultCursor(), new StringSelection("test")); + } + } +} --- /dev/null 2013-09-25 15:03:48.000000000 +0400 +++ new/test/java/awt/dnd/DropTargetEnterExitTest/MissedDragExitTest.java 2013-09-25 15:03:48.000000000 +0400 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8024163 + * @summary Checks that dragExit is generated when the new DropTarget is created under the drag + * @library ../../regtesthelpers + * @build Util + * @compile MissedDragExitTest.java + * @run main/othervm MissedDragExitTest + * @author Petr Pchelko + */ + +import test.java.awt.regtesthelpers.Util; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.event.InputEvent; + +public class MissedDragExitTest { + + private static final int FRAME_SIZE = 100; + private static final int FRAME_LOCATION = 100; + + private static volatile boolean dragExitCalled = false; + + private static volatile Frame f; + + private static void initAndShowUI() { + f = new Frame("Test frame"); + f.setBounds(FRAME_LOCATION,FRAME_LOCATION,FRAME_SIZE,FRAME_SIZE); + + final DraggablePanel dragSource = new DraggablePanel(); + dragSource.setBackground(Color.yellow); + DropTarget dt = new DropTarget(dragSource, new DropTargetAdapter() { + @Override public void drop(DropTargetDropEvent dtde) { } + + @Override + public void dragExit(DropTargetEvent dte) { + dragExitCalled = true; + } + + @Override + public void dragOver(DropTargetDragEvent dtde) { + Panel newDropTarget = new Panel(); + newDropTarget.setDropTarget(new DropTarget()); + newDropTarget.setBackground(Color.red); + newDropTarget.setBounds(0, 0, FRAME_SIZE, FRAME_SIZE); + dragSource.add(newDropTarget); + } + }); + dragSource.setDropTarget(dt); + f.add(dragSource); + + f.setVisible(true); + } + + public static void main(String[] args) throws Throwable { + try { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + initAndShowUI(); + } + }); + + Robot r = new Robot(); + Util.waitForIdle(r); + Util.drag(r, + new Point(FRAME_LOCATION + FRAME_SIZE / 3, FRAME_LOCATION + FRAME_SIZE / 3), + new Point(FRAME_LOCATION + FRAME_SIZE / 3 * 2, FRAME_LOCATION + FRAME_SIZE / 3 * 2), + InputEvent.BUTTON1_MASK); + Util.waitForIdle(r); + + if (!dragExitCalled) { + throw new RuntimeException("Failed. Drag exit was not called" ); + } + } finally { + if (f != null) { + f.dispose(); + } + } + } + + private static class DraggablePanel extends Panel implements DragGestureListener { + + public DraggablePanel() { + (new DragSource()).createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this); + } + + @Override + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(Cursor.getDefaultCursor(), new StringSelection("test")); + } + } +}