| /* |
| * Copyright (c) 2003, 2008, 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.Point; |
| |
| import java.awt.dnd.DnDConstants; |
| |
| import java.awt.event.MouseEvent; |
| |
| import java.io.IOException; |
| |
| import sun.util.logging.PlatformLogger; |
| |
| import sun.misc.Unsafe; |
| |
| /** |
| * XDropTargetProtocol implementation for XDnD protocol. |
| * |
| * @since 1.5 |
| */ |
| class XDnDDropTargetProtocol extends XDropTargetProtocol { |
| private static final PlatformLogger logger = |
| PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDropTargetProtocol"); |
| |
| private static final Unsafe unsafe = XlibWrapper.unsafe; |
| |
| private long sourceWindow = 0; |
| private long sourceWindowMask = 0; |
| private int sourceProtocolVersion = 0; |
| private int sourceActions = DnDConstants.ACTION_NONE; |
| private long[] sourceFormats = null; |
| private boolean trackSourceActions = false; |
| private int userAction = DnDConstants.ACTION_NONE; |
| private int sourceX = 0; |
| private int sourceY = 0; |
| private XWindow targetXWindow = null; |
| |
| // XEmbed stuff. |
| private long prevCtxt = 0; |
| private boolean overXEmbedClient = false; |
| |
| protected XDnDDropTargetProtocol(XDropTargetProtocolListener listener) { |
| super(listener); |
| } |
| |
| /** |
| * Creates an instance associated with the specified listener. |
| * |
| * @throws NullPointerException if listener is <code>null</code>. |
| */ |
| static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) { |
| return new XDnDDropTargetProtocol(listener); |
| } |
| |
| public String getProtocolName() { |
| return XDragAndDropProtocols.XDnD; |
| } |
| |
| public void registerDropTarget(long window) { |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| long data = Native.allocateLongArray(1); |
| |
| try { |
| Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION); |
| |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndAware.setAtomData(window, XAtom.XA_ATOM, data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != XConstants.Success) { |
| throw new XException("Cannot write XdndAware property"); |
| } |
| } finally { |
| unsafe.freeMemory(data); |
| data = 0; |
| } |
| } |
| |
| public void unregisterDropTarget(long window) { |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| XDnDConstants.XA_XdndAware.DeleteProperty(window); |
| } |
| |
| public void registerEmbedderDropSite(long embedder) { |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| boolean overriden = false; |
| int version = 0; |
| long proxy = 0; |
| long newProxy = XDropTargetRegistry.getDnDProxyWindow(); |
| int status = 0; |
| |
| WindowPropertyGetter wpg1 = |
| new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndAware, 0, 1, |
| false, XConstants.AnyPropertyType); |
| |
| try { |
| status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| |
| if (status == XConstants.Success && |
| wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { |
| |
| overriden = true; |
| version = (int)Native.getLong(wpg1.getData()); |
| } |
| } finally { |
| wpg1.dispose(); |
| } |
| |
| /* XdndProxy is not supported for prior to XDnD version 4 */ |
| if (overriden && version >= 4) { |
| WindowPropertyGetter wpg2 = |
| new WindowPropertyGetter(embedder, 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(); |
| } |
| } |
| } |
| |
| if (proxy == newProxy) { |
| // Embedder already registered. |
| return; |
| } |
| |
| long data = Native.allocateLongArray(1); |
| |
| try { |
| Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION); |
| |
| /* The proxy window must have the XdndAware set, as XDnD protocol |
| prescribes to check the proxy window for XdndAware. */ |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndAware.setAtomData(newProxy, XAtom.XA_ATOM, |
| data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != |
| XConstants.Success) { |
| throw new XException("Cannot write XdndAware property"); |
| } |
| |
| Native.putLong(data, 0, newProxy); |
| |
| /* The proxy window must have the XdndProxy set to point to itself.*/ |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndProxy.setAtomData(newProxy, XAtom.XA_WINDOW, |
| data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != |
| XConstants.Success) { |
| throw new XException("Cannot write XdndProxy property"); |
| } |
| |
| Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION); |
| |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM, |
| data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != |
| XConstants.Success) { |
| throw new XException("Cannot write XdndAware property"); |
| } |
| |
| Native.putLong(data, 0, newProxy); |
| |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW, |
| data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != |
| XConstants.Success) { |
| throw new XException("Cannot write XdndProxy property"); |
| } |
| } finally { |
| unsafe.freeMemory(data); |
| data = 0; |
| } |
| |
| putEmbedderRegistryEntry(embedder, overriden, version, proxy); |
| } |
| |
| public void unregisterEmbedderDropSite(long embedder) { |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder); |
| |
| if (entry == null) { |
| return; |
| } |
| |
| if (entry.isOverriden()) { |
| long data = Native.allocateLongArray(1); |
| |
| try { |
| Native.putLong(data, 0, entry.getVersion()); |
| |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM, |
| data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != |
| XConstants.Success) { |
| throw new XException("Cannot write XdndAware property"); |
| } |
| |
| Native.putLong(data, 0, (int)entry.getProxy()); |
| |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW, |
| data, 1); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != |
| XConstants.Success) { |
| throw new XException("Cannot write XdndProxy property"); |
| } |
| } finally { |
| unsafe.freeMemory(data); |
| data = 0; |
| } |
| } else { |
| XDnDConstants.XA_XdndAware.DeleteProperty(embedder); |
| XDnDConstants.XA_XdndProxy.DeleteProperty(embedder); |
| } |
| } |
| |
| /* |
| * Gets and stores in the registry the embedder's XDnD drop site info |
| * from the embedded. |
| */ |
| public void registerEmbeddedDropSite(long embedded) { |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| boolean overriden = false; |
| int version = 0; |
| long proxy = 0; |
| long newProxy = XDropTargetRegistry.getDnDProxyWindow(); |
| int status = 0; |
| |
| WindowPropertyGetter wpg1 = |
| new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndAware, 0, 1, |
| false, XConstants.AnyPropertyType); |
| |
| try { |
| status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| |
| if (status == XConstants.Success && |
| wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { |
| |
| overriden = true; |
| version = (int)Native.getLong(wpg1.getData()); |
| } |
| } finally { |
| wpg1.dispose(); |
| } |
| |
| /* XdndProxy is not supported for prior to XDnD version 4 */ |
| if (overriden && version >= 4) { |
| WindowPropertyGetter wpg2 = |
| new WindowPropertyGetter(embedded, 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(); |
| } |
| } |
| } |
| |
| putEmbedderRegistryEntry(embedded, overriden, version, proxy); |
| } |
| |
| public boolean isProtocolSupported(long window) { |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| WindowPropertyGetter wpg1 = |
| new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1, |
| false, XConstants.AnyPropertyType); |
| |
| try { |
| int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| |
| if (status == XConstants.Success && |
| wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { |
| |
| return true; |
| } else { |
| return false; |
| } |
| } finally { |
| wpg1.dispose(); |
| } |
| } |
| |
| private boolean processXdndEnter(XClientMessageEvent xclient) { |
| long source_win = 0; |
| long source_win_mask = 0; |
| int protocol_version = 0; |
| int actions = DnDConstants.ACTION_NONE; |
| boolean track = true; |
| long[] formats = null; |
| |
| if (getSourceWindow() != 0) { |
| return false; |
| } |
| |
| if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow) |
| && getEmbedderRegistryEntry(xclient.get_window()) == null) { |
| return false; |
| } |
| |
| if (xclient.get_message_type() != XDnDConstants.XA_XdndEnter.getAtom()){ |
| return false; |
| } |
| |
| protocol_version = |
| (int)((xclient.get_data(1) & XDnDConstants.XDND_PROTOCOL_MASK) >> |
| XDnDConstants.XDND_PROTOCOL_SHIFT); |
| |
| /* XDnD compliance only requires supporting version 3 and up. */ |
| if (protocol_version < XDnDConstants.XDND_MIN_PROTOCOL_VERSION) { |
| return false; |
| } |
| |
| /* Ignore the source if the protocol version is higher than we support. */ |
| if (protocol_version > XDnDConstants.XDND_PROTOCOL_VERSION) { |
| return false; |
| } |
| |
| source_win = xclient.get_data(0); |
| |
| /* Extract the list of supported actions. */ |
| if (protocol_version < 2) { |
| /* Prior to XDnD version 2 only COPY action was supported. */ |
| actions = DnDConstants.ACTION_COPY; |
| } else { |
| WindowPropertyGetter wpg = |
| new WindowPropertyGetter(source_win, |
| XDnDConstants.XA_XdndActionList, |
| 0, 0xFFFF, false, |
| XAtom.XA_ATOM); |
| try { |
| wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| |
| if (wpg.getActualType() == XAtom.XA_ATOM && |
| wpg.getActualFormat() == 32) { |
| long data = wpg.getData(); |
| |
| for (int i = 0; i < wpg.getNumberOfItems(); i++) { |
| actions |= |
| XDnDConstants.getJavaActionForXDnDAction(Native.getLong(data, i)); |
| } |
| } else { |
| /* |
| * According to XDnD protocol, XdndActionList is optional. |
| * If XdndActionList is not set we try to guess which actions are |
| * supported. |
| */ |
| actions = DnDConstants.ACTION_COPY; |
| track = true; |
| } |
| } finally { |
| wpg.dispose(); |
| } |
| } |
| |
| /* Extract the available data types. */ |
| if ((xclient.get_data(1) & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) { |
| WindowPropertyGetter wpg = |
| new WindowPropertyGetter(source_win, |
| XDnDConstants.XA_XdndTypeList, |
| 0, 0xFFFF, false, |
| XAtom.XA_ATOM); |
| try { |
| wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| |
| if (wpg.getActualType() == XAtom.XA_ATOM && |
| wpg.getActualFormat() == 32) { |
| formats = Native.toLongs(wpg.getData(), |
| wpg.getNumberOfItems()); |
| } else { |
| formats = new long[0]; |
| } |
| } finally { |
| wpg.dispose(); |
| } |
| } else { |
| int countFormats = 0; |
| long[] formats3 = new long[3]; |
| |
| for (int i = 0; i < 3; i++) { |
| long j; |
| if ((j = xclient.get_data(2 + i)) != XConstants.None) { |
| formats3[countFormats++] = j; |
| } |
| } |
| |
| formats = new long[countFormats]; |
| |
| System.arraycopy(formats3, 0, formats, 0, countFormats); |
| } |
| |
| assert XToolkit.isAWTLockHeldByCurrentThread(); |
| |
| /* |
| * Select for StructureNotifyMask to receive DestroyNotify in case of source |
| * crash. |
| */ |
| XWindowAttributes wattr = new XWindowAttributes(); |
| try { |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), |
| source_win, wattr.pData); |
| |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (status == 0 || |
| (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != XConstants.Success)) { |
| throw new XException("XGetWindowAttributes failed"); |
| } |
| |
| source_win_mask = wattr.get_your_event_mask(); |
| } finally { |
| wattr.dispose(); |
| } |
| |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win, |
| source_win_mask | |
| XConstants.StructureNotifyMask); |
| |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != XConstants.Success) { |
| throw new XException("XSelectInput failed"); |
| } |
| |
| sourceWindow = source_win; |
| sourceWindowMask = source_win_mask; |
| sourceProtocolVersion = protocol_version; |
| sourceActions = actions; |
| sourceFormats = formats; |
| trackSourceActions = track; |
| |
| return true; |
| } |
| |
| private boolean processXdndPosition(XClientMessageEvent xclient) { |
| long time_stamp = (int)XConstants.CurrentTime; |
| long xdnd_action = 0; |
| int java_action = DnDConstants.ACTION_NONE; |
| int x = 0; |
| int y = 0; |
| |
| /* Ignore XDnD messages from all other windows. */ |
| if (sourceWindow != xclient.get_data(0)) { |
| return false; |
| } |
| |
| XWindow xwindow = null; |
| { |
| XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window()); |
| if (xbasewindow instanceof XWindow) { |
| xwindow = (XWindow)xbasewindow; |
| } |
| } |
| |
| x = (int)(xclient.get_data(2) >> 16); |
| y = (int)(xclient.get_data(2) & 0xFFFF); |
| |
| if (xwindow == null) { |
| long receiver = |
| XDropTargetRegistry.getRegistry().getEmbeddedDropSite( |
| xclient.get_window(), x, y); |
| |
| if (receiver != 0) { |
| XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver); |
| if (xbasewindow instanceof XWindow) { |
| xwindow = (XWindow)xbasewindow; |
| } |
| } |
| } |
| |
| if (xwindow != null) { |
| /* Translate mouse position from root coordinates |
| to the target window coordinates. */ |
| Point p = xwindow.toLocal(x, y); |
| x = p.x; |
| y = p.y; |
| } |
| |
| /* Time stamp - new in XDnD version 1. */ |
| if (sourceProtocolVersion > 0) { |
| time_stamp = xclient.get_data(3); |
| } |
| |
| /* User action - new in XDnD version 2. */ |
| if (sourceProtocolVersion > 1) { |
| xdnd_action = xclient.get_data(4); |
| } else { |
| /* The default action is XdndActionCopy */ |
| xdnd_action = XDnDConstants.XA_XdndActionCopy.getAtom(); |
| } |
| |
| java_action = XDnDConstants.getJavaActionForXDnDAction(xdnd_action); |
| |
| if (trackSourceActions) { |
| sourceActions |= java_action; |
| } |
| |
| if (xwindow == null) { |
| if (targetXWindow != null) { |
| notifyProtocolListener(targetXWindow, x, y, |
| DnDConstants.ACTION_NONE, xclient, |
| MouseEvent.MOUSE_EXITED); |
| } |
| } else { |
| int java_event_id = 0; |
| |
| if (targetXWindow == null) { |
| java_event_id = MouseEvent.MOUSE_ENTERED; |
| } else { |
| java_event_id = MouseEvent.MOUSE_DRAGGED; |
| } |
| |
| notifyProtocolListener(xwindow, x, y, java_action, xclient, |
| java_event_id); |
| } |
| |
| userAction = java_action; |
| sourceX = x; |
| sourceY = y; |
| targetXWindow = xwindow; |
| |
| return true; |
| } |
| |
| private boolean processXdndLeave(XClientMessageEvent xclient) { |
| /* Ignore XDnD messages from all other windows. */ |
| if (sourceWindow != xclient.get_data(0)) { |
| return false; |
| } |
| |
| cleanup(); |
| |
| return true; |
| } |
| |
| private boolean processXdndDrop(XClientMessageEvent xclient) { |
| /* Ignore XDnD messages from all other windows. */ |
| if (sourceWindow != xclient.get_data(0)) { |
| return false; |
| } |
| |
| if (targetXWindow != null) { |
| notifyProtocolListener(targetXWindow, sourceX, sourceY, userAction, |
| xclient, MouseEvent.MOUSE_RELEASED); |
| } |
| |
| return true; |
| } |
| |
| public int getMessageType(XClientMessageEvent xclient) { |
| long messageType = xclient.get_message_type(); |
| |
| if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) { |
| return ENTER_MESSAGE; |
| } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) { |
| return MOTION_MESSAGE; |
| } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) { |
| return LEAVE_MESSAGE; |
| } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) { |
| return DROP_MESSAGE; |
| } else { |
| return UNKNOWN_MESSAGE; |
| } |
| } |
| |
| protected boolean processClientMessageImpl(XClientMessageEvent xclient) { |
| long messageType = xclient.get_message_type(); |
| |
| if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) { |
| return processXdndEnter(xclient); |
| } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) { |
| return processXdndPosition(xclient); |
| } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) { |
| return processXdndLeave(xclient); |
| } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) { |
| return processXdndDrop(xclient); |
| } else { |
| return false; |
| } |
| } |
| |
| protected void sendEnterMessageToToplevel(long toplevel, |
| XClientMessageEvent xclient) { |
| /* flags */ |
| long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT; |
| if (sourceFormats != null && sourceFormats.length > 3) { |
| data1 |= XDnDConstants.XDND_DATA_TYPES_BIT; |
| } |
| long data2 = sourceFormats.length > 0 ? sourceFormats[0] : 0; |
| long data3 = sourceFormats.length > 1 ? sourceFormats[1] : 0; |
| long data4 = sourceFormats.length > 2 ? sourceFormats[2] : 0; |
| sendEnterMessageToToplevelImpl(toplevel, xclient.get_data(0), |
| data1, data2, data3, data4); |
| |
| } |
| |
| private void sendEnterMessageToToplevelImpl(long toplevel, |
| long sourceWindow, |
| long data1, long data2, |
| long data3, long data4) { |
| XClientMessageEvent enter = new XClientMessageEvent(); |
| try { |
| enter.set_type((int)XConstants.ClientMessage); |
| enter.set_window(toplevel); |
| enter.set_format(32); |
| enter.set_message_type(XDnDConstants.XA_XdndEnter.getAtom()); |
| /* XID of the source window */ |
| enter.set_data(0, sourceWindow); |
| enter.set_data(1, data1); |
| enter.set_data(2, data2); |
| enter.set_data(3, data3); |
| enter.set_data(4, data4); |
| |
| forwardClientMessageToToplevel(toplevel, enter); |
| } finally { |
| enter.dispose(); |
| } |
| } |
| |
| protected void sendLeaveMessageToToplevel(long toplevel, |
| XClientMessageEvent xclient) { |
| sendLeaveMessageToToplevelImpl(toplevel, xclient.get_data(0)); |
| } |
| |
| protected void sendLeaveMessageToToplevelImpl(long toplevel, |
| long sourceWindow) { |
| XClientMessageEvent leave = new XClientMessageEvent(); |
| try { |
| leave.set_type((int)XConstants.ClientMessage); |
| leave.set_window(toplevel); |
| leave.set_format(32); |
| leave.set_message_type(XDnDConstants.XA_XdndLeave.getAtom()); |
| /* XID of the source window */ |
| leave.set_data(0, sourceWindow); |
| /* flags */ |
| leave.set_data(1, 0); |
| |
| forwardClientMessageToToplevel(toplevel, leave); |
| } finally { |
| leave.dispose(); |
| } |
| } |
| |
| public boolean sendResponse(long ctxt, int eventID, int action) { |
| XClientMessageEvent xclient = new XClientMessageEvent(ctxt); |
| |
| if (xclient.get_message_type() != |
| XDnDConstants.XA_XdndPosition.getAtom()) { |
| |
| return false; |
| } |
| |
| if (eventID == MouseEvent.MOUSE_EXITED) { |
| action = DnDConstants.ACTION_NONE; |
| } |
| |
| XClientMessageEvent msg = new XClientMessageEvent(); |
| try { |
| msg.set_type((int)XConstants.ClientMessage); |
| msg.set_window(xclient.get_data(0)); |
| msg.set_format(32); |
| msg.set_message_type(XDnDConstants.XA_XdndStatus.getAtom()); |
| /* target window */ |
| msg.set_data(0, xclient.get_window()); |
| /* flags */ |
| long flags = 0; |
| if (action != DnDConstants.ACTION_NONE) { |
| flags |= XDnDConstants.XDND_ACCEPT_DROP_FLAG; |
| } |
| msg.set_data(1, flags); |
| /* specify an empty rectangle */ |
| msg.set_data(2, 0); /* x, y */ |
| msg.set_data(3, 0); /* w, h */ |
| /* action accepted by the target */ |
| msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(action)); |
| |
| XToolkit.awtLock(); |
| try { |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), |
| xclient.get_data(0), |
| false, XConstants.NoEventMask, |
| msg.pData); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| } finally { |
| msg.dispose(); |
| } |
| |
| return true; |
| } |
| |
| public Object getData(long ctxt, long format) |
| throws IllegalArgumentException, IOException { |
| XClientMessageEvent xclient = new XClientMessageEvent(ctxt); |
| long message_type = xclient.get_message_type(); |
| long time_stamp = XConstants.CurrentTime; |
| |
| // NOTE: we assume that the source supports at least version 1, so we |
| // can use the time stamp |
| if (message_type == XDnDConstants.XA_XdndPosition.getAtom()) { |
| // X server time is an unsigned 32-bit number! |
| time_stamp = xclient.get_data(3) & 0xFFFFFFFFL; |
| } else if (message_type == XDnDConstants.XA_XdndDrop.getAtom()) { |
| // X server time is an unsigned 32-bit number! |
| time_stamp = xclient.get_data(2) & 0xFFFFFFFFL; |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| |
| return XDnDConstants.XDnDSelection.getData(format, time_stamp); |
| } |
| |
| public boolean sendDropDone(long ctxt, boolean success, int dropAction) { |
| XClientMessageEvent xclient = new XClientMessageEvent(ctxt); |
| |
| if (xclient.get_message_type() != |
| XDnDConstants.XA_XdndDrop.getAtom()) { |
| return false; |
| } |
| |
| /* |
| * The XDnD protocol recommends that the target requests the special |
| * target DELETE in case if the drop action is XdndActionMove. |
| */ |
| if (dropAction == DnDConstants.ACTION_MOVE && success) { |
| |
| long time_stamp = xclient.get_data(2); |
| long xdndSelectionAtom = |
| XDnDConstants.XDnDSelection.getSelectionAtom().getAtom(); |
| |
| XToolkit.awtLock(); |
| try { |
| XlibWrapper.XConvertSelection(XToolkit.getDisplay(), |
| xdndSelectionAtom, |
| XAtom.get("DELETE").getAtom(), |
| XAtom.get("XAWT_SELECTION").getAtom(), |
| XWindow.getXAWTRootWindow().getWindow(), |
| time_stamp); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| } |
| |
| XClientMessageEvent msg = new XClientMessageEvent(); |
| try { |
| msg.set_type((int)XConstants.ClientMessage); |
| msg.set_window(xclient.get_data(0)); |
| msg.set_format(32); |
| msg.set_message_type(XDnDConstants.XA_XdndFinished.getAtom()); |
| msg.set_data(0, xclient.get_window()); /* target window */ |
| msg.set_data(1, 0); /* flags */ |
| /* specify an empty rectangle */ |
| msg.set_data(2, 0); |
| if (sourceProtocolVersion >= 5) { |
| if (success) { |
| msg.set_data(1, XDnDConstants.XDND_ACCEPT_DROP_FLAG); |
| } |
| /* action performed by the target */ |
| msg.set_data(2, XDnDConstants.getXDnDActionForJavaAction(dropAction)); |
| } |
| msg.set_data(3, 0); |
| msg.set_data(4, 0); |
| |
| XToolkit.awtLock(); |
| try { |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), |
| xclient.get_data(0), |
| false, XConstants.NoEventMask, |
| msg.pData); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| } finally { |
| msg.dispose(); |
| } |
| |
| /* |
| * Flush the buffer to guarantee that the drop completion event is sent |
| * to the source before the method returns. |
| */ |
| XToolkit.awtLock(); |
| try { |
| XlibWrapper.XFlush(XToolkit.getDisplay()); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| |
| /* Trick to prevent cleanup() from posting dragExit */ |
| targetXWindow = null; |
| |
| /* Cannot do cleanup before the drop finishes as we may need |
| source protocol version to send drop finished message. */ |
| cleanup(); |
| return true; |
| } |
| |
| public final long getSourceWindow() { |
| return sourceWindow; |
| } |
| |
| /** |
| * Reset the state of the object. |
| */ |
| public void cleanup() { |
| // Clear the reference to this protocol. |
| XDropTargetEventProcessor.reset(); |
| |
| if (targetXWindow != null) { |
| notifyProtocolListener(targetXWindow, 0, 0, |
| DnDConstants.ACTION_NONE, null, |
| MouseEvent.MOUSE_EXITED); |
| } |
| |
| if (sourceWindow != 0) { |
| XToolkit.awtLock(); |
| try { |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow, |
| sourceWindowMask); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| } |
| |
| sourceWindow = 0; |
| sourceWindowMask = 0; |
| sourceProtocolVersion = 0; |
| sourceActions = DnDConstants.ACTION_NONE; |
| sourceFormats = null; |
| trackSourceActions = false; |
| userAction = DnDConstants.ACTION_NONE; |
| sourceX = 0; |
| sourceY = 0; |
| targetXWindow = null; |
| } |
| |
| public boolean isDragOverComponent() { |
| return targetXWindow != null; |
| } |
| |
| public void adjustEventForForwarding(XClientMessageEvent xclient, |
| EmbedderRegistryEntry entry) { |
| /* Adjust the event to match the XDnD protocol version. */ |
| int version = entry.getVersion(); |
| if (xclient.get_message_type() == XDnDConstants.XA_XdndEnter.getAtom()) { |
| int min_version = sourceProtocolVersion < version ? |
| sourceProtocolVersion : version; |
| long data1 = min_version << XDnDConstants.XDND_PROTOCOL_SHIFT; |
| if (sourceFormats != null && sourceFormats.length > 3) { |
| data1 |= XDnDConstants.XDND_DATA_TYPES_BIT; |
| } |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" " |
| + " entryVersion=" + version |
| + " sourceProtocolVersion=" + |
| sourceProtocolVersion |
| + " sourceFormats.length=" + |
| (sourceFormats != null ? sourceFormats.length : 0)); |
| } |
| xclient.set_data(1, data1); |
| } |
| } |
| |
| private void notifyProtocolListener(XWindow xwindow, int x, int y, |
| int dropAction, |
| XClientMessageEvent xclient, |
| int eventID) { |
| long nativeCtxt = 0; |
| |
| // Make a copy of the passed XClientMessageEvent structure, since |
| // the original structure can be freed before this |
| // SunDropTargetEvent is dispatched. |
| if (xclient != null) { |
| int size = new XClientMessageEvent(nativeCtxt).getSize(); |
| |
| nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize()); |
| |
| unsafe.copyMemory(xclient.pData, nativeCtxt, size); |
| |
| long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT; |
| if (sourceFormats != null && sourceFormats.length > 3) { |
| data1 |= XDnDConstants.XDND_DATA_TYPES_BIT; |
| } |
| // Append information from the latest XdndEnter event. |
| Native.putLong(nativeCtxt + size, data1); |
| Native.putLong(nativeCtxt + size + Native.getLongSize(), |
| sourceFormats.length > 0 ? sourceFormats[0] : 0); |
| Native.putLong(nativeCtxt + size + 2 * Native.getLongSize(), |
| sourceFormats.length > 1 ? sourceFormats[1] : 0); |
| Native.putLong(nativeCtxt + size + 3 * Native.getLongSize(), |
| sourceFormats.length > 2 ? sourceFormats[2] : 0); |
| } |
| |
| getProtocolListener().handleDropTargetNotification(xwindow, x, y, |
| dropAction, |
| sourceActions, |
| sourceFormats, |
| nativeCtxt, |
| eventID); |
| } |
| |
| /* |
| * The methods/fields defined below are executed/accessed only on |
| * the toolkit thread. |
| * The methods/fields defined below are executed/accessed only on the event |
| * dispatch thread. |
| */ |
| |
| public boolean forwardEventToEmbedded(long embedded, long ctxt, |
| int eventID) { |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" ctxt=" + ctxt + |
| " type=" + (ctxt != 0 ? |
| getMessageType(new |
| XClientMessageEvent(ctxt)) : 0) + |
| " prevCtxt=" + prevCtxt + |
| " prevType=" + (prevCtxt != 0 ? |
| getMessageType(new |
| XClientMessageEvent(prevCtxt)) : 0)); |
| } |
| if ((ctxt == 0 || |
| getMessageType(new XClientMessageEvent(ctxt)) == UNKNOWN_MESSAGE) && |
| (prevCtxt == 0 || |
| getMessageType(new XClientMessageEvent(prevCtxt)) == UNKNOWN_MESSAGE)) { |
| return false; |
| } |
| |
| // The size of XClientMessageEvent structure. |
| int size = XClientMessageEvent.getSize(); |
| |
| if (ctxt != 0) { |
| XClientMessageEvent xclient = new XClientMessageEvent(ctxt); |
| if (!overXEmbedClient) { |
| long data1 = Native.getLong(ctxt + size); |
| long data2 = Native.getLong(ctxt + size + Native.getLongSize()); |
| long data3 = Native.getLong(ctxt + size + 2 * Native.getLongSize()); |
| long data4 = Native.getLong(ctxt + size + 3 * Native.getLongSize()); |
| |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" 1 " |
| + " embedded=" + embedded |
| + " source=" + xclient.get_data(0) |
| + " data1=" + data1 |
| + " data2=" + data2 |
| + " data3=" + data3 |
| + " data4=" + data4); |
| } |
| |
| // Copy XdndTypeList from source to proxy. |
| if ((data1 & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) { |
| WindowPropertyGetter wpg = |
| new WindowPropertyGetter(xclient.get_data(0), |
| XDnDConstants.XA_XdndTypeList, |
| 0, 0xFFFF, false, |
| XAtom.XA_ATOM); |
| try { |
| wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); |
| |
| if (wpg.getActualType() == XAtom.XA_ATOM && |
| wpg.getActualFormat() == 32) { |
| |
| XToolkit.awtLock(); |
| try { |
| XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); |
| XDnDConstants.XA_XdndTypeList.setAtomData(xclient.get_window(), |
| XAtom.XA_ATOM, |
| wpg.getData(), |
| wpg.getNumberOfItems()); |
| XToolkit.RESTORE_XERROR_HANDLER(); |
| |
| if (XToolkit.saved_error != null && |
| XToolkit.saved_error.get_error_code() != XConstants.Success) { |
| if (logger.isLoggable(PlatformLogger.WARNING)) { |
| logger.warning("Cannot set XdndTypeList on the proxy window"); |
| } |
| } |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| } else { |
| if (logger.isLoggable(PlatformLogger.WARNING)) { |
| logger.warning("Cannot read XdndTypeList from the source window"); |
| } |
| } |
| } finally { |
| wpg.dispose(); |
| } |
| } |
| XDragSourceContextPeer.setProxyModeSourceWindow(xclient.get_data(0)); |
| |
| sendEnterMessageToToplevelImpl(embedded, xclient.get_window(), |
| data1, data2, data3, data4); |
| overXEmbedClient = true; |
| } |
| |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" 2 " |
| + " embedded=" + embedded |
| + " xclient=" + xclient); |
| } |
| |
| /* Make a copy of the original event, since we are going to modify the |
| event while it still can be referenced from other Java events. */ |
| { |
| XClientMessageEvent copy = new XClientMessageEvent(); |
| unsafe.copyMemory(xclient.pData, copy.pData, copy.getSize()); |
| |
| copy.set_data(0, xclient.get_window()); |
| |
| forwardClientMessageToToplevel(embedded, copy); |
| } |
| } |
| |
| if (eventID == MouseEvent.MOUSE_EXITED) { |
| if (overXEmbedClient) { |
| if (ctxt != 0 || prevCtxt != 0) { |
| // Last chance to send XdndLeave to the XEmbed client. |
| XClientMessageEvent xclient = ctxt != 0 ? |
| new XClientMessageEvent(ctxt) : |
| new XClientMessageEvent(prevCtxt); |
| sendLeaveMessageToToplevelImpl(embedded, xclient.get_window()); |
| } |
| overXEmbedClient = false; |
| // We have to clear the proxy mode source window here, |
| // when the drag exits the XEmbedCanvasPeer. |
| // NOTE: at this point the XEmbed client still might have some |
| // drag notifications to process and it will send responses to |
| // us. With the proxy mode source window cleared we won't be |
| // able to forward these responses to the actual source. This is |
| // not a problem if the drag operation was initiated in this |
| // JVM. However, if it was initiated in another processes the |
| // responses will be lost. We bear with it for now, as it seems |
| // there is no other reliable point to clear. |
| XDragSourceContextPeer.setProxyModeSourceWindow(0); |
| } |
| } |
| |
| if (eventID == MouseEvent.MOUSE_RELEASED) { |
| overXEmbedClient = false; |
| cleanup(); |
| } |
| |
| if (prevCtxt != 0) { |
| unsafe.freeMemory(prevCtxt); |
| prevCtxt = 0; |
| } |
| |
| if (ctxt != 0 && overXEmbedClient) { |
| prevCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize()); |
| |
| unsafe.copyMemory(ctxt, prevCtxt, size + 4 * Native.getLongSize()); |
| } |
| |
| return true; |
| } |
| |
| public boolean isXEmbedSupported() { |
| return true; |
| } |
| } |