| /* | |
| * Copyright (c) 2003, 2014, 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. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * 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. | |
| */ | |
| package sun.awt.X11; | |
| import java.awt.datatransfer.Transferable; | |
| import java.awt.datatransfer.DataFlavor; | |
| import java.awt.dnd.DnDConstants; | |
| import java.awt.dnd.InvalidDnDOperationException; | |
| import java.util.Map; | |
| import sun.util.logging.PlatformLogger; | |
| import jdk.internal.misc.Unsafe; | |
| /** | |
| * XDragSourceProtocol implementation for XDnD protocol. | |
| * | |
| * @since 1.5 | |
| */ | |
| final class XDnDDragSourceProtocol extends XDragSourceProtocol { | |
| private static final PlatformLogger logger = | |
| PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDragSourceProtocol"); | |
| private static final Unsafe unsafe = XlibWrapper.unsafe; | |
| protected XDnDDragSourceProtocol(XDragSourceProtocolListener listener) { | |
| super(listener); | |
| } | |
| /** | |
| * Creates an instance associated with the specified listener. | |
| * | |
| * @throws NullPointerException if listener is {@code null}. | |
| */ | |
| static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) { | |
| return new XDnDDragSourceProtocol(listener); | |
| } | |
| @Override | |
| public String getProtocolName() { | |
| return XDragAndDropProtocols.XDnD; | |
| } | |
| /** | |
| * Performs protocol-specific drag initialization. | |
| * | |
| * @return true if the initialized successfully. | |
| */ | |
| @Override | |
| protected void initializeDragImpl(int actions, Transferable contents, | |
| Map<Long, DataFlavor> formatMap, long[] formats) | |
| throws InvalidDnDOperationException, | |
| IllegalArgumentException, XException { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| long window = XDragSourceProtocol.getDragSourceWindow(); | |
| long data = Native.allocateLongArray(3); | |
| int action_count = 0; | |
| try { | |
| if ((actions & DnDConstants.ACTION_COPY) != 0) { | |
| Native.putLong(data, action_count, | |
| XDnDConstants.XA_XdndActionCopy.getAtom()); | |
| action_count++; | |
| } | |
| if ((actions & DnDConstants.ACTION_MOVE) != 0) { | |
| Native.putLong(data, action_count, | |
| XDnDConstants.XA_XdndActionMove.getAtom()); | |
| action_count++; | |
| } | |
| if ((actions & DnDConstants.ACTION_LINK) != 0) { | |
| Native.putLong(data, action_count, | |
| XDnDConstants.XA_XdndActionLink.getAtom()); | |
| action_count++; | |
| } | |
| XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); | |
| XDnDConstants.XA_XdndActionList.setAtomData(window, | |
| XAtom.XA_ATOM, | |
| data, action_count); | |
| XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); | |
| if ((XErrorHandlerUtil.saved_error) != null && | |
| (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { | |
| cleanup(); | |
| throw new XException("Cannot write XdndActionList property"); | |
| } | |
| } finally { | |
| unsafe.freeMemory(data); | |
| data = 0; | |
| } | |
| data = Native.allocateLongArray(formats.length); | |
| try { | |
| Native.put(data, formats); | |
| XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); | |
| XDnDConstants.XA_XdndTypeList.setAtomData(window, | |
| XAtom.XA_ATOM, | |
| data, formats.length); | |
| XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); | |
| if ((XErrorHandlerUtil.saved_error != null) && | |
| (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { | |
| cleanup(); | |
| throw new XException("Cannot write XdndActionList property"); | |
| } | |
| } finally { | |
| unsafe.freeMemory(data); | |
| data = 0; | |
| } | |
| if (!XDnDConstants.XDnDSelection.setOwner(contents, formatMap, formats, | |
| XConstants.CurrentTime)) { | |
| cleanup(); | |
| throw new InvalidDnDOperationException("Cannot acquire selection ownership"); | |
| } | |
| } | |
| private boolean processXdndStatus(XClientMessageEvent xclient) { | |
| int action = DnDConstants.ACTION_NONE; | |
| /* Ignore XDnD messages from all other windows. */ | |
| if (xclient.get_data(0) != getTargetWindow()) { | |
| return true; | |
| } | |
| if ((xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0) { | |
| /* This feature is new in XDnD version 2, but we can use it as XDnD | |
| compliance only requires supporting version 3 and up. */ | |
| action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(4)); | |
| } | |
| getProtocolListener().handleDragReply(action); | |
| return true; | |
| } | |
| private boolean processXdndFinished(XClientMessageEvent xclient) { | |
| /* Ignore XDnD messages from all other windows. */ | |
| if (xclient.get_data(0) != getTargetWindow()) { | |
| return true; | |
| } | |
| if (getTargetProtocolVersion() >= 5) { | |
| boolean success = (xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0; | |
| int action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(2)); | |
| getProtocolListener().handleDragFinished(success, action); | |
| } else { | |
| getProtocolListener().handleDragFinished(); | |
| } | |
| finalizeDrop(); | |
| return true; | |
| } | |
| @Override | |
| public boolean processClientMessage(XClientMessageEvent xclient) { | |
| if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom()) { | |
| return processXdndStatus(xclient); | |
| } else if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) { | |
| return processXdndFinished(xclient); | |
| } else { | |
| return false; | |
| } | |
| } | |
| @Override | |
| public TargetWindowInfo getTargetWindowInfo(long window) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| WindowPropertyGetter wpg1 = | |
| new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1, | |
| false, XConstants.AnyPropertyType); | |
| int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); | |
| if (status == XConstants.Success && | |
| wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { | |
| int targetVersion = (int)Native.getLong(wpg1.getData()); | |
| wpg1.dispose(); | |
| if (targetVersion >= XDnDConstants.XDND_MIN_PROTOCOL_VERSION) { | |
| long proxy = 0; | |
| int protocolVersion = | |
| targetVersion < XDnDConstants.XDND_PROTOCOL_VERSION ? | |
| targetVersion : XDnDConstants.XDND_PROTOCOL_VERSION; | |
| WindowPropertyGetter wpg2 = | |
| new WindowPropertyGetter(window, XDnDConstants.XA_XdndProxy, | |
| 0, 1, false, XAtom.XA_WINDOW); | |
| try { | |
| status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); | |
| if (status == XConstants.Success && | |
| wpg2.getData() != 0 && | |
| wpg2.getActualType() == XAtom.XA_WINDOW) { | |
| proxy = Native.getLong(wpg2.getData()); | |
| } | |
| } finally { | |
| wpg2.dispose(); | |
| } | |
| if (proxy != 0) { | |
| WindowPropertyGetter wpg3 = | |
| new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy, | |
| 0, 1, false, XAtom.XA_WINDOW); | |
| try { | |
| status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); | |
| if (status != XConstants.Success || | |
| wpg3.getData() == 0 || | |
| wpg3.getActualType() != XAtom.XA_WINDOW || | |
| Native.getLong(wpg3.getData()) != proxy) { | |
| proxy = 0; | |
| } else { | |
| WindowPropertyGetter wpg4 = | |
| new WindowPropertyGetter(proxy, | |
| XDnDConstants.XA_XdndAware, | |
| 0, 1, false, | |
| XConstants.AnyPropertyType); | |
| try { | |
| status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); | |
| if (status != XConstants.Success || | |
| wpg4.getData() == 0 || | |
| wpg4.getActualType() != XAtom.XA_ATOM) { | |
| proxy = 0; | |
| } | |
| } finally { | |
| wpg4.dispose(); | |
| } | |
| } | |
| } finally { | |
| wpg3.dispose(); | |
| } | |
| } | |
| return new TargetWindowInfo(proxy, protocolVersion); | |
| } | |
| } else { | |
| wpg1.dispose(); | |
| } | |
| return null; | |
| } | |
| @Override | |
| public void sendEnterMessage(long[] formats, | |
| int sourceAction, int sourceActions, long time) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| assert getTargetWindow() != 0; | |
| assert formats != null; | |
| XClientMessageEvent msg = new XClientMessageEvent(); | |
| try { | |
| msg.set_type(XConstants.ClientMessage); | |
| msg.set_window(getTargetWindow()); | |
| msg.set_format(32); | |
| msg.set_message_type(XDnDConstants.XA_XdndEnter.getAtom()); | |
| msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); | |
| long data1 = | |
| getTargetProtocolVersion() << XDnDConstants.XDND_PROTOCOL_SHIFT; | |
| data1 |= formats.length > 3 ? XDnDConstants.XDND_DATA_TYPES_BIT : 0; | |
| msg.set_data(1, data1); | |
| msg.set_data(2, formats.length > 0 ? formats[0] : 0); | |
| msg.set_data(3, formats.length > 1 ? formats[1] : 0); | |
| msg.set_data(4, formats.length > 2 ? formats[2] : 0); | |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), | |
| getTargetProxyWindow(), | |
| false, XConstants.NoEventMask, | |
| msg.pData); | |
| } finally { | |
| msg.dispose(); | |
| } | |
| } | |
| @Override | |
| public void sendMoveMessage(int xRoot, int yRoot, | |
| int sourceAction, int sourceActions, long time) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| assert getTargetWindow() != 0; | |
| XClientMessageEvent msg = new XClientMessageEvent(); | |
| try { | |
| msg.set_type(XConstants.ClientMessage); | |
| msg.set_window(getTargetWindow()); | |
| msg.set_format(32); | |
| msg.set_message_type(XDnDConstants.XA_XdndPosition.getAtom()); | |
| msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); | |
| msg.set_data(1, 0); /* flags */ | |
| msg.set_data(2, xRoot << 16 | yRoot); | |
| msg.set_data(3, time); | |
| msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(sourceAction)); | |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), | |
| getTargetProxyWindow(), | |
| false, XConstants.NoEventMask, | |
| msg.pData); | |
| } finally { | |
| msg.dispose(); | |
| } | |
| } | |
| @Override | |
| public void sendLeaveMessage(long time) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| assert getTargetWindow() != 0; | |
| XClientMessageEvent msg = new XClientMessageEvent(); | |
| try { | |
| msg.set_type(XConstants.ClientMessage); | |
| msg.set_window(getTargetWindow()); | |
| msg.set_format(32); | |
| msg.set_message_type(XDnDConstants.XA_XdndLeave.getAtom()); | |
| msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); | |
| msg.set_data(1, 0); | |
| msg.set_data(2, 0); | |
| msg.set_data(3, 0); | |
| msg.set_data(4, 0); | |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), | |
| getTargetProxyWindow(), | |
| false, XConstants.NoEventMask, | |
| msg.pData); | |
| } finally { | |
| msg.dispose(); | |
| } | |
| } | |
| @Override | |
| public void sendDropMessage(int xRoot, int yRoot, | |
| int sourceAction, int sourceActions, | |
| long time) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| assert getTargetWindow() != 0; | |
| XClientMessageEvent msg = new XClientMessageEvent(); | |
| try { | |
| msg.set_type(XConstants.ClientMessage); | |
| msg.set_window(getTargetWindow()); | |
| msg.set_format(32); | |
| msg.set_message_type(XDnDConstants.XA_XdndDrop.getAtom()); | |
| msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); | |
| msg.set_data(1, 0); /* flags */ | |
| msg.set_data(2, time); | |
| msg.set_data(3, 0); | |
| msg.set_data(4, 0); | |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), | |
| getTargetProxyWindow(), | |
| false, XConstants.NoEventMask, | |
| msg.pData); | |
| } finally { | |
| msg.dispose(); | |
| } | |
| } | |
| @Override | |
| public boolean processProxyModeEvent(XClientMessageEvent xclient, | |
| long sourceWindow) { | |
| if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom() || | |
| xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) { | |
| if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) { | |
| XDragSourceContextPeer.setProxyModeSourceWindow(0); | |
| } | |
| // This can happen if the drag operation started in the XEmbed server. | |
| // In this case there is no need to forward it elsewhere, we should | |
| // process it here. | |
| if (xclient.get_window() == sourceWindow) { | |
| return false; | |
| } | |
| if (logger.isLoggable(PlatformLogger.Level.FINEST)) { | |
| logger.finest(" sourceWindow=" + sourceWindow + | |
| " get_window=" + xclient.get_window() + | |
| " xclient=" + xclient); | |
| } | |
| xclient.set_data(0, xclient.get_window()); | |
| xclient.set_window(sourceWindow); | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), sourceWindow, | |
| false, XConstants.NoEventMask, | |
| xclient.pData); | |
| return true; | |
| } | |
| return false; | |
| } | |
| // TODO: register this runnable with XDnDSelection. | |
| public void run() { | |
| // XdndSelection ownership lost. | |
| cleanup(); | |
| } | |
| } |