1 /*
2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.awt.X11;
27
28 import java.awt.datatransfer.Transferable;
29 import java.awt.datatransfer.DataFlavor;
30
31 import java.awt.dnd.DnDConstants;
32 import java.awt.dnd.InvalidDnDOperationException;
33
34 import java.util.Map;
35
36 import sun.util.logging.PlatformLogger;
37
38 import sun.misc.Unsafe;
39
40 /**
41 * XDragSourceProtocol implementation for XDnD protocol.
42 *
43 * @since 1.5
44 */
45 class XDnDDragSourceProtocol extends XDragSourceProtocol {
46 private static final PlatformLogger logger =
47 PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDragSourceProtocol");
48
49 private static final Unsafe unsafe = XlibWrapper.unsafe;
50
51 protected XDnDDragSourceProtocol(XDragSourceProtocolListener listener) {
52 super(listener);
53 }
54
55 /**
56 * Creates an instance associated with the specified listener.
57 *
58 * @throws NullPointerException if listener is {@code null}.
59 */
60 static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) {
61 return new XDnDDragSourceProtocol(listener);
62 }
63
64 public String getProtocolName() {
65 return XDragAndDropProtocols.XDnD;
66 }
67
68 /**
69 * Performs protocol-specific drag initialization.
70 *
71 * @return true if the initialized successfully.
72 */
73 protected void initializeDragImpl(int actions, Transferable contents,
74 Map<Long, DataFlavor> formatMap, long[] formats)
75 throws InvalidDnDOperationException,
76 IllegalArgumentException, XException {
77 assert XToolkit.isAWTLockHeldByCurrentThread();
78
79 long window = XDragSourceProtocol.getDragSourceWindow();
80
81 long data = Native.allocateLongArray(3);
82 int action_count = 0;
83 try {
84 if ((actions & DnDConstants.ACTION_COPY) != 0) {
85 Native.putLong(data, action_count,
86 XDnDConstants.XA_XdndActionCopy.getAtom());
87 action_count++;
88 }
89 if ((actions & DnDConstants.ACTION_MOVE) != 0) {
90 Native.putLong(data, action_count,
91 XDnDConstants.XA_XdndActionMove.getAtom());
92 action_count++;
93 }
94 if ((actions & DnDConstants.ACTION_LINK) != 0) {
95 Native.putLong(data, action_count,
96 XDnDConstants.XA_XdndActionLink.getAtom());
97 action_count++;
98 }
99
100 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
101 XDnDConstants.XA_XdndActionList.setAtomData(window,
102 XAtom.XA_ATOM,
103 data, action_count);
104 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
105
106 if ((XErrorHandlerUtil.saved_error) != null &&
107 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
108 cleanup();
109 throw new XException("Cannot write XdndActionList property");
110 }
111 } finally {
112 unsafe.freeMemory(data);
113 data = 0;
114 }
115
116 data = Native.allocateLongArray(formats.length);
117
118 try {
119 Native.put(data, formats);
120
121 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
122 XDnDConstants.XA_XdndTypeList.setAtomData(window,
123 XAtom.XA_ATOM,
124 data, formats.length);
125 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
126
127 if ((XErrorHandlerUtil.saved_error != null) &&
128 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
129 cleanup();
130 throw new XException("Cannot write XdndActionList property");
131 }
132 } finally {
133 unsafe.freeMemory(data);
134 data = 0;
135 }
136
137 if (!XDnDConstants.XDnDSelection.setOwner(contents, formatMap, formats,
138 XConstants.CurrentTime)) {
139 cleanup();
140 throw new InvalidDnDOperationException("Cannot acquire selection ownership");
141 }
142 }
143
144 private boolean processXdndStatus(XClientMessageEvent xclient) {
145 int action = DnDConstants.ACTION_NONE;
146
147 /* Ignore XDnD messages from all other windows. */
148 if (xclient.get_data(0) != getTargetWindow()) {
149 return true;
150 }
151
152 if ((xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0) {
153 /* This feature is new in XDnD version 2, but we can use it as XDnD
154 compliance only requires supporting version 3 and up. */
155 action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(4));
156 }
157
158 getProtocolListener().handleDragReply(action);
159
160 return true;
161 }
162
163 private boolean processXdndFinished(XClientMessageEvent xclient) {
164 /* Ignore XDnD messages from all other windows. */
165 if (xclient.get_data(0) != getTargetWindow()) {
166 return true;
167 }
168
169 if (getTargetProtocolVersion() >= 5) {
170 boolean success = (xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0;
171 int action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(2));
172 getProtocolListener().handleDragFinished(success, action);
173 } else {
174 getProtocolListener().handleDragFinished();
175 }
176
177 finalizeDrop();
178
179 return true;
180 }
181
182 public boolean processClientMessage(XClientMessageEvent xclient) {
183 if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom()) {
184 return processXdndStatus(xclient);
185 } else if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) {
186 return processXdndFinished(xclient);
187 } else {
188 return false;
189 }
190 }
191
192 public TargetWindowInfo getTargetWindowInfo(long window) {
193 assert XToolkit.isAWTLockHeldByCurrentThread();
194
195 WindowPropertyGetter wpg1 =
196 new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1,
197 false, XConstants.AnyPropertyType);
198
199 int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
200
201 if (status == XConstants.Success &&
202 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
203
204 int targetVersion = (int)Native.getLong(wpg1.getData());
205
206 wpg1.dispose();
207
208 if (targetVersion >= XDnDConstants.XDND_MIN_PROTOCOL_VERSION) {
209 long proxy = 0;
210 int protocolVersion =
211 targetVersion < XDnDConstants.XDND_PROTOCOL_VERSION ?
212 targetVersion : XDnDConstants.XDND_PROTOCOL_VERSION;
213
214 WindowPropertyGetter wpg2 =
215 new WindowPropertyGetter(window, XDnDConstants.XA_XdndProxy,
216 0, 1, false, XAtom.XA_WINDOW);
217
218 try {
219 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
220
221 if (status == XConstants.Success &&
222 wpg2.getData() != 0 &&
223 wpg2.getActualType() == XAtom.XA_WINDOW) {
224
225 proxy = Native.getLong(wpg2.getData());
226 }
227 } finally {
228 wpg2.dispose();
229 }
230
231 if (proxy != 0) {
232 WindowPropertyGetter wpg3 =
233 new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
234 0, 1, false, XAtom.XA_WINDOW);
235
236 try {
237 status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
238
239 if (status != XConstants.Success ||
240 wpg3.getData() == 0 ||
241 wpg3.getActualType() != XAtom.XA_WINDOW ||
242 Native.getLong(wpg3.getData()) != proxy) {
243
244 proxy = 0;
245 } else {
246 WindowPropertyGetter wpg4 =
247 new WindowPropertyGetter(proxy,
248 XDnDConstants.XA_XdndAware,
249 0, 1, false,
250 XConstants.AnyPropertyType);
251
252 try {
253 status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
254
255 if (status != XConstants.Success ||
256 wpg4.getData() == 0 ||
257 wpg4.getActualType() != XAtom.XA_ATOM) {
258
259 proxy = 0;
260 }
261 } finally {
262 wpg4.dispose();
263 }
264 }
265 } finally {
266 wpg3.dispose();
267 }
268 }
269
270 return new TargetWindowInfo(proxy, protocolVersion);
271 }
272 } else {
273 wpg1.dispose();
274 }
275
276 return null;
277 }
278
279 public void sendEnterMessage(long[] formats,
280 int sourceAction, int sourceActions, long time) {
281 assert XToolkit.isAWTLockHeldByCurrentThread();
282 assert getTargetWindow() != 0;
283 assert formats != null;
284
285 XClientMessageEvent msg = new XClientMessageEvent();
286 try {
287 msg.set_type(XConstants.ClientMessage);
288 msg.set_window(getTargetWindow());
289 msg.set_format(32);
290 msg.set_message_type(XDnDConstants.XA_XdndEnter.getAtom());
291 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
292 long data1 =
293 getTargetProtocolVersion() << XDnDConstants.XDND_PROTOCOL_SHIFT;
294 data1 |= formats.length > 3 ? XDnDConstants.XDND_DATA_TYPES_BIT : 0;
295 msg.set_data(1, data1);
296 msg.set_data(2, formats.length > 0 ? formats[0] : 0);
297 msg.set_data(3, formats.length > 1 ? formats[1] : 0);
298 msg.set_data(4, formats.length > 2 ? formats[2] : 0);
299 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
300 getTargetProxyWindow(),
301 false, XConstants.NoEventMask,
302 msg.pData);
303 } finally {
304 msg.dispose();
305 }
306 }
307
308 public void sendMoveMessage(int xRoot, int yRoot,
309 int sourceAction, int sourceActions, long time) {
310 assert XToolkit.isAWTLockHeldByCurrentThread();
311 assert getTargetWindow() != 0;
312
313 XClientMessageEvent msg = new XClientMessageEvent();
314 try {
315 msg.set_type(XConstants.ClientMessage);
316 msg.set_window(getTargetWindow());
317 msg.set_format(32);
318 msg.set_message_type(XDnDConstants.XA_XdndPosition.getAtom());
319 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
320 msg.set_data(1, 0); /* flags */
321 msg.set_data(2, xRoot << 16 | yRoot);
322 msg.set_data(3, time);
323 msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(sourceAction));
324 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
325 getTargetProxyWindow(),
326 false, XConstants.NoEventMask,
327 msg.pData);
328 } finally {
329 msg.dispose();
330 }
331 }
332
333 public void sendLeaveMessage(long time) {
334 assert XToolkit.isAWTLockHeldByCurrentThread();
335 assert getTargetWindow() != 0;
336
337 XClientMessageEvent msg = new XClientMessageEvent();
338 try {
339 msg.set_type(XConstants.ClientMessage);
340 msg.set_window(getTargetWindow());
341 msg.set_format(32);
342 msg.set_message_type(XDnDConstants.XA_XdndLeave.getAtom());
343 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
344 msg.set_data(1, 0);
345 msg.set_data(2, 0);
346 msg.set_data(3, 0);
347 msg.set_data(4, 0);
348 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
349 getTargetProxyWindow(),
350 false, XConstants.NoEventMask,
351 msg.pData);
352 } finally {
353 msg.dispose();
354 }
355 }
356
357 public void sendDropMessage(int xRoot, int yRoot,
358 int sourceAction, int sourceActions,
359 long time) {
360 assert XToolkit.isAWTLockHeldByCurrentThread();
361 assert getTargetWindow() != 0;
362
363 XClientMessageEvent msg = new XClientMessageEvent();
364 try {
365 msg.set_type(XConstants.ClientMessage);
366 msg.set_window(getTargetWindow());
367 msg.set_format(32);
368 msg.set_message_type(XDnDConstants.XA_XdndDrop.getAtom());
369 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
370 msg.set_data(1, 0); /* flags */
371 msg.set_data(2, time);
372 msg.set_data(3, 0);
373 msg.set_data(4, 0);
374 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
375 getTargetProxyWindow(),
376 false, XConstants.NoEventMask,
377 msg.pData);
378 } finally {
379 msg.dispose();
380 }
381 }
382
383 public boolean processProxyModeEvent(XClientMessageEvent xclient,
384 long sourceWindow) {
385 if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom() ||
386 xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) {
387
388 if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) {
389 XDragSourceContextPeer.setProxyModeSourceWindow(0);
390 }
391
392 // This can happen if the drag operation started in the XEmbed server.
393 // In this case there is no need to forward it elsewhere, we should
394 // process it here.
395 if (xclient.get_window() == sourceWindow) {
396 return false;
397 }
398
399 if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
400 logger.finest(" sourceWindow=" + sourceWindow +
401 " get_window=" + xclient.get_window() +
402 " xclient=" + xclient);
403 }
404 xclient.set_data(0, xclient.get_window());
405 xclient.set_window(sourceWindow);
406
407 assert XToolkit.isAWTLockHeldByCurrentThread();
408
409 XlibWrapper.XSendEvent(XToolkit.getDisplay(), sourceWindow,
410 false, XConstants.NoEventMask,
411 xclient.pData);
412
413 return true;
414 }
415
416 return false;
417 }
418
419 // TODO: register this runnable with XDnDSelection.
420 public void run() {
421 // XdndSelection ownership lost.
422 cleanup();
423 }
424 }
--- EOF ---