/*
* Copyright (c) 2003, 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. 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.dnd.DnDConstants;
import java.awt.dnd.InvalidDnDOperationException;
import java.util.Map;
import sun.util.logging.PlatformLogger;
import sun.misc.Unsafe;
/**
* XDragSourceProtocol implementation for XDnD protocol.
*
* @since 1.5
*/
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</code>.
*/
static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) {
return new XDnDDragSourceProtocol(listener);
}
public String getProtocolName() {
return XDragAndDropProtocols.XDnD;
}
/**
* Performs protocol-specific drag initialization.
*
* @returns true if the initialized successfully.
*/
protected void initializeDragImpl(int actions, Transferable contents,
Map 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;
}
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;
}
}
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;
}
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((int)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();
}
}
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((int)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();
}
}
public void sendLeaveMessage(long time) {
assert XToolkit.isAWTLockHeldByCurrentThread();
assert getTargetWindow() != 0;
XClientMessageEvent msg = new XClientMessageEvent();
try {
msg.set_type((int)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();
}
}
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((int)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();
}
}
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.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();
}
}