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