blob: 00fe0430d366531bf69cf6a7ff844f12da618b11 [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.Point;
29
30import java.awt.dnd.DnDConstants;
31
32import java.awt.event.MouseEvent;
33
34import java.io.IOException;
35
36import java.util.logging.*;
37
38import sun.misc.Unsafe;
39
40/**
41 * XDropTargetProtocol implementation for XDnD protocol.
42 *
43 * @since 1.5
44 */
45class XDnDDropTargetProtocol extends XDropTargetProtocol {
46 private static final Logger logger =
47 Logger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDropTargetProtocol");
48
49 private static final Unsafe unsafe = XlibWrapper.unsafe;
50
51 private long sourceWindow = 0;
52 private long sourceWindowMask = 0;
53 private int sourceProtocolVersion = 0;
54 private int sourceActions = DnDConstants.ACTION_NONE;
55 private long[] sourceFormats = null;
56 private boolean trackSourceActions = false;
57 private int userAction = DnDConstants.ACTION_NONE;
58 private int sourceX = 0;
59 private int sourceY = 0;
60 private XWindow targetXWindow = null;
61
62 // XEmbed stuff.
63 private long prevCtxt = 0;
64 private boolean overXEmbedClient = false;
65
66 protected XDnDDropTargetProtocol(XDropTargetProtocolListener listener) {
67 super(listener);
68 }
69
70 /**
71 * Creates an instance associated with the specified listener.
72 *
73 * @throws NullPointerException if listener is <code>null</code>.
74 */
75 static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
76 return new XDnDDropTargetProtocol(listener);
77 }
78
79 public String getProtocolName() {
80 return XDragAndDropProtocols.XDnD;
81 }
82
83 public void registerDropTarget(long window) {
84 assert XToolkit.isAWTLockHeldByCurrentThread();
85
86 long data = Native.allocateLongArray(1);
87
88 try {
89 Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
90
91 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
92 XDnDConstants.XA_XdndAware.setAtomData(window, XAtom.XA_ATOM, data, 1);
93 XToolkit.RESTORE_XERROR_HANDLER();
94
95 if (XToolkit.saved_error != null &&
96 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
97 throw new XException("Cannot write XdndAware property");
98 }
99 } finally {
100 unsafe.freeMemory(data);
101 data = 0;
102 }
103 }
104
105 public void unregisterDropTarget(long window) {
106 assert XToolkit.isAWTLockHeldByCurrentThread();
107
108 XDnDConstants.XA_XdndAware.DeleteProperty(window);
109 }
110
111 public void registerEmbedderDropSite(long embedder) {
112 assert XToolkit.isAWTLockHeldByCurrentThread();
113
114 boolean overriden = false;
115 int version = 0;
116 long proxy = 0;
117 long newProxy = XDropTargetRegistry.getDnDProxyWindow();
118 int status = 0;
119
120 WindowPropertyGetter wpg1 =
121 new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndAware, 0, 1,
122 false, XlibWrapper.AnyPropertyType);
123
124 try {
125 status = wpg1.execute(XToolkit.IgnoreBadWindowHandler);
126
127 if (status == XlibWrapper.Success &&
128 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
129
130 overriden = true;
131 version = (int)Native.getLong(wpg1.getData());
132 }
133 } finally {
134 wpg1.dispose();
135 }
136
137 /* XdndProxy is not supported for prior to XDnD version 4 */
138 if (overriden && version >= 4) {
139 WindowPropertyGetter wpg2 =
140 new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndProxy,
141 0, 1, false, XAtom.XA_WINDOW);
142
143 try {
144 status = wpg2.execute(XToolkit.IgnoreBadWindowHandler);
145
146 if (status == XlibWrapper.Success &&
147 wpg2.getData() != 0 &&
148 wpg2.getActualType() == XAtom.XA_WINDOW) {
149
150 proxy = Native.getLong(wpg2.getData());
151 }
152 } finally {
153 wpg2.dispose();
154 }
155
156 if (proxy != 0) {
157 WindowPropertyGetter wpg3 =
158 new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
159 0, 1, false, XAtom.XA_WINDOW);
160
161 try {
162 status = wpg3.execute(XToolkit.IgnoreBadWindowHandler);
163
164 if (status != XlibWrapper.Success ||
165 wpg3.getData() == 0 ||
166 wpg3.getActualType() != XAtom.XA_WINDOW ||
167 Native.getLong(wpg3.getData()) != proxy) {
168
169 proxy = 0;
170 } else {
171 WindowPropertyGetter wpg4 =
172 new WindowPropertyGetter(proxy,
173 XDnDConstants.XA_XdndAware,
174 0, 1, false,
175 XlibWrapper.AnyPropertyType);
176
177 try {
178 status = wpg4.execute(XToolkit.IgnoreBadWindowHandler);
179
180 if (status != XlibWrapper.Success ||
181 wpg4.getData() == 0 ||
182 wpg4.getActualType() != XAtom.XA_ATOM) {
183
184 proxy = 0;
185 }
186 } finally {
187 wpg4.dispose();
188 }
189 }
190 } finally {
191 wpg3.dispose();
192 }
193 }
194 }
195
196 if (proxy == newProxy) {
197 // Embedder already registered.
198 return;
199 }
200
201 long data = Native.allocateLongArray(1);
202
203 try {
204 Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
205
206 /* The proxy window must have the XdndAware set, as XDnD protocol
207 prescribes to check the proxy window for XdndAware. */
208 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
209 XDnDConstants.XA_XdndAware.setAtomData(newProxy, XAtom.XA_ATOM,
210 data, 1);
211 XToolkit.RESTORE_XERROR_HANDLER();
212
213 if (XToolkit.saved_error != null &&
214 XToolkit.saved_error.get_error_code() !=
215 XlibWrapper.Success) {
216 throw new XException("Cannot write XdndAware property");
217 }
218
219 Native.putLong(data, 0, newProxy);
220
221 /* The proxy window must have the XdndProxy set to point to itself.*/
222 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
223 XDnDConstants.XA_XdndProxy.setAtomData(newProxy, XAtom.XA_WINDOW,
224 data, 1);
225 XToolkit.RESTORE_XERROR_HANDLER();
226
227 if (XToolkit.saved_error != null &&
228 XToolkit.saved_error.get_error_code() !=
229 XlibWrapper.Success) {
230 throw new XException("Cannot write XdndProxy property");
231 }
232
233 Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
234
235 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
236 XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM,
237 data, 1);
238 XToolkit.RESTORE_XERROR_HANDLER();
239
240 if (XToolkit.saved_error != null &&
241 XToolkit.saved_error.get_error_code() !=
242 XlibWrapper.Success) {
243 throw new XException("Cannot write XdndAware property");
244 }
245
246 Native.putLong(data, 0, newProxy);
247
248 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
249 XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW,
250 data, 1);
251 XToolkit.RESTORE_XERROR_HANDLER();
252
253 if (XToolkit.saved_error != null &&
254 XToolkit.saved_error.get_error_code() !=
255 XlibWrapper.Success) {
256 throw new XException("Cannot write XdndProxy property");
257 }
258 } finally {
259 unsafe.freeMemory(data);
260 data = 0;
261 }
262
263 putEmbedderRegistryEntry(embedder, overriden, version, proxy);
264 }
265
266 public void unregisterEmbedderDropSite(long embedder) {
267 assert XToolkit.isAWTLockHeldByCurrentThread();
268
269 EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder);
270
271 if (entry == null) {
272 return;
273 }
274
275 if (entry.isOverriden()) {
276 long data = Native.allocateLongArray(1);
277
278 try {
279 Native.putLong(data, 0, entry.getVersion());
280
281 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
282 XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM,
283 data, 1);
284 XToolkit.RESTORE_XERROR_HANDLER();
285
286 if (XToolkit.saved_error != null &&
287 XToolkit.saved_error.get_error_code() !=
288 XlibWrapper.Success) {
289 throw new XException("Cannot write XdndAware property");
290 }
291
292 Native.putLong(data, 0, (int)entry.getProxy());
293
294 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
295 XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW,
296 data, 1);
297 XToolkit.RESTORE_XERROR_HANDLER();
298
299 if (XToolkit.saved_error != null &&
300 XToolkit.saved_error.get_error_code() !=
301 XlibWrapper.Success) {
302 throw new XException("Cannot write XdndProxy property");
303 }
304 } finally {
305 unsafe.freeMemory(data);
306 data = 0;
307 }
308 } else {
309 XDnDConstants.XA_XdndAware.DeleteProperty(embedder);
310 XDnDConstants.XA_XdndProxy.DeleteProperty(embedder);
311 }
312 }
313
314 /*
315 * Gets and stores in the registry the embedder's XDnD drop site info
316 * from the embedded.
317 */
318 public void registerEmbeddedDropSite(long embedded) {
319 assert XToolkit.isAWTLockHeldByCurrentThread();
320
321 boolean overriden = false;
322 int version = 0;
323 long proxy = 0;
324 long newProxy = XDropTargetRegistry.getDnDProxyWindow();
325 int status = 0;
326
327 WindowPropertyGetter wpg1 =
328 new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndAware, 0, 1,
329 false, XlibWrapper.AnyPropertyType);
330
331 try {
332 status = wpg1.execute(XToolkit.IgnoreBadWindowHandler);
333
334 if (status == XlibWrapper.Success &&
335 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
336
337 overriden = true;
338 version = (int)Native.getLong(wpg1.getData());
339 }
340 } finally {
341 wpg1.dispose();
342 }
343
344 /* XdndProxy is not supported for prior to XDnD version 4 */
345 if (overriden && version >= 4) {
346 WindowPropertyGetter wpg2 =
347 new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndProxy,
348 0, 1, false, XAtom.XA_WINDOW);
349
350 try {
351 status = wpg2.execute(XToolkit.IgnoreBadWindowHandler);
352
353 if (status == XlibWrapper.Success &&
354 wpg2.getData() != 0 &&
355 wpg2.getActualType() == XAtom.XA_WINDOW) {
356
357 proxy = Native.getLong(wpg2.getData());
358 }
359 } finally {
360 wpg2.dispose();
361 }
362
363 if (proxy != 0) {
364 WindowPropertyGetter wpg3 =
365 new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
366 0, 1, false, XAtom.XA_WINDOW);
367
368 try {
369 status = wpg3.execute(XToolkit.IgnoreBadWindowHandler);
370
371 if (status != XlibWrapper.Success ||
372 wpg3.getData() == 0 ||
373 wpg3.getActualType() != XAtom.XA_WINDOW ||
374 Native.getLong(wpg3.getData()) != proxy) {
375
376 proxy = 0;
377 } else {
378 WindowPropertyGetter wpg4 =
379 new WindowPropertyGetter(proxy,
380 XDnDConstants.XA_XdndAware,
381 0, 1, false,
382 XlibWrapper.AnyPropertyType);
383
384 try {
385 status = wpg4.execute(XToolkit.IgnoreBadWindowHandler);
386
387 if (status != XlibWrapper.Success ||
388 wpg4.getData() == 0 ||
389 wpg4.getActualType() != XAtom.XA_ATOM) {
390
391 proxy = 0;
392 }
393 } finally {
394 wpg4.dispose();
395 }
396 }
397 } finally {
398 wpg3.dispose();
399 }
400 }
401 }
402
403 putEmbedderRegistryEntry(embedded, overriden, version, proxy);
404 }
405
406 public boolean isProtocolSupported(long window) {
407 assert XToolkit.isAWTLockHeldByCurrentThread();
408
409 WindowPropertyGetter wpg1 =
410 new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1,
411 false, XlibWrapper.AnyPropertyType);
412
413 try {
414 int status = wpg1.execute(XToolkit.IgnoreBadWindowHandler);
415
416 if (status == XlibWrapper.Success &&
417 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
418
419 return true;
420 } else {
421 return false;
422 }
423 } finally {
424 wpg1.dispose();
425 }
426 }
427
428 private boolean processXdndEnter(XClientMessageEvent xclient) {
429 long source_win = 0;
430 long source_win_mask = 0;
431 int protocol_version = 0;
432 int actions = DnDConstants.ACTION_NONE;
433 boolean track = true;
434 long[] formats = null;
435
436 if (getSourceWindow() != 0) {
437 return false;
438 }
439
440 if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow)
441 && getEmbedderRegistryEntry(xclient.get_window()) == null) {
442 return false;
443 }
444
445 if (xclient.get_message_type() != XDnDConstants.XA_XdndEnter.getAtom()){
446 return false;
447 }
448
449 protocol_version =
450 (int)((xclient.get_data(1) & XDnDConstants.XDND_PROTOCOL_MASK) >>
451 XDnDConstants.XDND_PROTOCOL_SHIFT);
452
453 /* XDnD compliance only requires supporting version 3 and up. */
454 if (protocol_version < XDnDConstants.XDND_MIN_PROTOCOL_VERSION) {
455 return false;
456 }
457
458 /* Ignore the source if the protocol version is higher than we support. */
459 if (protocol_version > XDnDConstants.XDND_PROTOCOL_VERSION) {
460 return false;
461 }
462
463 source_win = xclient.get_data(0);
464
465 /* Extract the list of supported actions. */
466 if (protocol_version < 2) {
467 /* Prior to XDnD version 2 only COPY action was supported. */
468 actions = DnDConstants.ACTION_COPY;
469 } else {
470 WindowPropertyGetter wpg =
471 new WindowPropertyGetter(source_win,
472 XDnDConstants.XA_XdndActionList,
473 0, 0xFFFF, false,
474 XAtom.XA_ATOM);
475 try {
476 wpg.execute(XToolkit.IgnoreBadWindowHandler);
477
478 if (wpg.getActualType() == XAtom.XA_ATOM &&
479 wpg.getActualFormat() == 32) {
480 long data = wpg.getData();
481
482 for (int i = 0; i < wpg.getNumberOfItems(); i++) {
483 actions |=
484 XDnDConstants.getJavaActionForXDnDAction(Native.getLong(data, i));
485 }
486 } else {
487 /*
488 * According to XDnD protocol, XdndActionList is optional.
489 * If XdndActionList is not set we try to guess which actions are
490 * supported.
491 */
492 actions = DnDConstants.ACTION_COPY;
493 track = true;
494 }
495 } finally {
496 wpg.dispose();
497 }
498 }
499
500 /* Extract the available data types. */
501 if ((xclient.get_data(1) & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) {
502 WindowPropertyGetter wpg =
503 new WindowPropertyGetter(source_win,
504 XDnDConstants.XA_XdndTypeList,
505 0, 0xFFFF, false,
506 XAtom.XA_ATOM);
507 try {
508 wpg.execute(XToolkit.IgnoreBadWindowHandler);
509
510 if (wpg.getActualType() == XAtom.XA_ATOM &&
511 wpg.getActualFormat() == 32) {
512 formats = Native.toLongs(wpg.getData(),
513 wpg.getNumberOfItems());
514 } else {
515 formats = new long[0];
516 }
517 } finally {
518 wpg.dispose();
519 }
520 } else {
521 int countFormats = 0;
522 long[] formats3 = new long[3];
523
524 for (int i = 0; i < 3; i++) {
525 long j;
526 if ((j = xclient.get_data(2 + i)) != XlibWrapper.None) {
527 formats3[countFormats++] = j;
528 }
529 }
530
531 formats = new long[countFormats];
532
533 System.arraycopy(formats3, 0, formats, 0, countFormats);
534 }
535
536 assert XToolkit.isAWTLockHeldByCurrentThread();
537
538 /*
539 * Select for StructureNotifyMask to receive DestroyNotify in case of source
540 * crash.
541 */
542 XWindowAttributes wattr = new XWindowAttributes();
543 try {
544 XToolkit.WITH_XERROR_HANDLER(XToolkit.IgnoreBadWindowHandler);
545 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
546 source_win, wattr.pData);
547
548 XToolkit.RESTORE_XERROR_HANDLER();
549
550 if (status == 0 ||
551 (XToolkit.saved_error != null &&
552 XToolkit.saved_error.get_error_code() != XlibWrapper.Success)) {
553 throw new XException("XGetWindowAttributes failed");
554 }
555
556 source_win_mask = wattr.get_your_event_mask();
557 } finally {
558 wattr.dispose();
559 }
560
561 XToolkit.WITH_XERROR_HANDLER(XToolkit.IgnoreBadWindowHandler);
562 XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win,
563 source_win_mask |
564 XlibWrapper.StructureNotifyMask);
565
566 XToolkit.RESTORE_XERROR_HANDLER();
567
568 if (XToolkit.saved_error != null &&
569 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
570 throw new XException("XSelectInput failed");
571 }
572
573 sourceWindow = source_win;
574 sourceWindowMask = source_win_mask;
575 sourceProtocolVersion = protocol_version;
576 sourceActions = actions;
577 sourceFormats = formats;
578 trackSourceActions = track;
579
580 return true;
581 }
582
583 private boolean processXdndPosition(XClientMessageEvent xclient) {
584 long time_stamp = (int)XlibWrapper.CurrentTime;
585 long xdnd_action = 0;
586 int java_action = DnDConstants.ACTION_NONE;
587 int x = 0;
588 int y = 0;
589
590 /* Ignore XDnD messages from all other windows. */
591 if (sourceWindow != xclient.get_data(0)) {
592 return false;
593 }
594
595 XWindow xwindow = null;
596 {
597 XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
598 if (xbasewindow instanceof XWindow) {
599 xwindow = (XWindow)xbasewindow;
600 }
601 }
602
603 x = (int)(xclient.get_data(2) >> 16);
604 y = (int)(xclient.get_data(2) & 0xFFFF);
605
606 if (xwindow == null) {
607 long receiver =
608 XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
609 xclient.get_window(), x, y);
610
611 if (receiver != 0) {
612 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
613 if (xbasewindow instanceof XWindow) {
614 xwindow = (XWindow)xbasewindow;
615 }
616 }
617 }
618
619 if (xwindow != null) {
620 /* Translate mouse position from root coordinates
621 to the target window coordinates. */
622 Point p = xwindow.toLocal(x, y);
623 x = p.x;
624 y = p.y;
625 }
626
627 /* Time stamp - new in XDnD version 1. */
628 if (sourceProtocolVersion > 0) {
629 time_stamp = xclient.get_data(3);
630 }
631
632 /* User action - new in XDnD version 2. */
633 if (sourceProtocolVersion > 1) {
634 xdnd_action = xclient.get_data(4);
635 } else {
636 /* The default action is XdndActionCopy */
637 xdnd_action = XDnDConstants.XA_XdndActionCopy.getAtom();
638 }
639
640 java_action = XDnDConstants.getJavaActionForXDnDAction(xdnd_action);
641
642 if (trackSourceActions) {
643 sourceActions |= java_action;
644 }
645
646 if (xwindow == null) {
647 if (targetXWindow != null) {
648 notifyProtocolListener(targetXWindow, x, y,
649 DnDConstants.ACTION_NONE, xclient,
650 MouseEvent.MOUSE_EXITED);
651 }
652 } else {
653 int java_event_id = 0;
654
655 if (targetXWindow == null) {
656 java_event_id = MouseEvent.MOUSE_ENTERED;
657 } else {
658 java_event_id = MouseEvent.MOUSE_DRAGGED;
659 }
660
661 notifyProtocolListener(xwindow, x, y, java_action, xclient,
662 java_event_id);
663 }
664
665 userAction = java_action;
666 sourceX = x;
667 sourceY = y;
668 targetXWindow = xwindow;
669
670 return true;
671 }
672
673 private boolean processXdndLeave(XClientMessageEvent xclient) {
674 /* Ignore XDnD messages from all other windows. */
675 if (sourceWindow != xclient.get_data(0)) {
676 return false;
677 }
678
679 cleanup();
680
681 return true;
682 }
683
684 private boolean processXdndDrop(XClientMessageEvent xclient) {
685 /* Ignore XDnD messages from all other windows. */
686 if (sourceWindow != xclient.get_data(0)) {
687 return false;
688 }
689
690 if (targetXWindow != null) {
691 notifyProtocolListener(targetXWindow, sourceX, sourceY, userAction,
692 xclient, MouseEvent.MOUSE_RELEASED);
693 }
694
695 return true;
696 }
697
698 public int getMessageType(XClientMessageEvent xclient) {
699 long messageType = xclient.get_message_type();
700
701 if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) {
702 return ENTER_MESSAGE;
703 } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) {
704 return MOTION_MESSAGE;
705 } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) {
706 return LEAVE_MESSAGE;
707 } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) {
708 return DROP_MESSAGE;
709 } else {
710 return UNKNOWN_MESSAGE;
711 }
712 }
713
714 protected boolean processClientMessageImpl(XClientMessageEvent xclient) {
715 long messageType = xclient.get_message_type();
716
717 if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) {
718 return processXdndEnter(xclient);
719 } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) {
720 return processXdndPosition(xclient);
721 } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) {
722 return processXdndLeave(xclient);
723 } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) {
724 return processXdndDrop(xclient);
725 } else {
726 return false;
727 }
728 }
729
730 protected void sendEnterMessageToToplevel(long toplevel,
731 XClientMessageEvent xclient) {
732 /* flags */
733 long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT;
734 if (sourceFormats != null && sourceFormats.length > 3) {
735 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
736 }
737 long data2 = sourceFormats.length > 0 ? sourceFormats[0] : 0;
738 long data3 = sourceFormats.length > 1 ? sourceFormats[1] : 0;
739 long data4 = sourceFormats.length > 2 ? sourceFormats[2] : 0;
740 sendEnterMessageToToplevelImpl(toplevel, xclient.get_data(0),
741 data1, data2, data3, data4);
742
743 }
744
745 private void sendEnterMessageToToplevelImpl(long toplevel,
746 long sourceWindow,
747 long data1, long data2,
748 long data3, long data4) {
749 XClientMessageEvent enter = new XClientMessageEvent();
750 try {
751 enter.set_type((int)XlibWrapper.ClientMessage);
752 enter.set_window(toplevel);
753 enter.set_format(32);
754 enter.set_message_type(XDnDConstants.XA_XdndEnter.getAtom());
755 /* XID of the source window */
756 enter.set_data(0, sourceWindow);
757 enter.set_data(1, data1);
758 enter.set_data(2, data2);
759 enter.set_data(3, data3);
760 enter.set_data(4, data4);
761
762 forwardClientMessageToToplevel(toplevel, enter);
763 } finally {
764 enter.dispose();
765 }
766 }
767
768 protected void sendLeaveMessageToToplevel(long toplevel,
769 XClientMessageEvent xclient) {
770 sendLeaveMessageToToplevelImpl(toplevel, xclient.get_data(0));
771 }
772
773 protected void sendLeaveMessageToToplevelImpl(long toplevel,
774 long sourceWindow) {
775 XClientMessageEvent leave = new XClientMessageEvent();
776 try {
777 leave.set_type((int)XlibWrapper.ClientMessage);
778 leave.set_window(toplevel);
779 leave.set_format(32);
780 leave.set_message_type(XDnDConstants.XA_XdndLeave.getAtom());
781 /* XID of the source window */
782 leave.set_data(0, sourceWindow);
783 /* flags */
784 leave.set_data(1, 0);
785
786 forwardClientMessageToToplevel(toplevel, leave);
787 } finally {
788 leave.dispose();
789 }
790 }
791
792 public boolean sendResponse(long ctxt, int eventID, int action) {
793 XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
794
795 if (xclient.get_message_type() !=
796 XDnDConstants.XA_XdndPosition.getAtom()) {
797
798 return false;
799 }
800
801 if (eventID == MouseEvent.MOUSE_EXITED) {
802 action = DnDConstants.ACTION_NONE;
803 }
804
805 XClientMessageEvent msg = new XClientMessageEvent();
806 try {
807 msg.set_type((int)XlibWrapper.ClientMessage);
808 msg.set_window(xclient.get_data(0));
809 msg.set_format(32);
810 msg.set_message_type(XDnDConstants.XA_XdndStatus.getAtom());
811 /* target window */
812 msg.set_data(0, xclient.get_window());
813 /* flags */
814 long flags = 0;
815 if (action != DnDConstants.ACTION_NONE) {
816 flags |= XDnDConstants.XDND_ACCEPT_DROP_FLAG;
817 }
818 msg.set_data(1, flags);
819 /* specify an empty rectangle */
820 msg.set_data(2, 0); /* x, y */
821 msg.set_data(3, 0); /* w, h */
822 /* action accepted by the target */
823 msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(action));
824
825 XToolkit.awtLock();
826 try {
827 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
828 xclient.get_data(0),
829 false, XlibWrapper.NoEventMask,
830 msg.pData);
831 } finally {
832 XToolkit.awtUnlock();
833 }
834 } finally {
835 msg.dispose();
836 }
837
838 return true;
839 }
840
841 public Object getData(long ctxt, long format)
842 throws IllegalArgumentException, IOException {
843 XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
844 long message_type = xclient.get_message_type();
845 long time_stamp = XlibWrapper.CurrentTime;
846
847 // NOTE: we assume that the source supports at least version 1, so we
848 // can use the time stamp
849 if (message_type == XDnDConstants.XA_XdndPosition.getAtom()) {
850 // X server time is an unsigned 32-bit number!
851 time_stamp = xclient.get_data(3) & 0xFFFFFFFFL;
852 } else if (message_type == XDnDConstants.XA_XdndDrop.getAtom()) {
853 // X server time is an unsigned 32-bit number!
854 time_stamp = xclient.get_data(2) & 0xFFFFFFFFL;
855 } else {
856 throw new IllegalArgumentException();
857 }
858
859 return XDnDConstants.XDnDSelection.getData(format, time_stamp);
860 }
861
862 public boolean sendDropDone(long ctxt, boolean success, int dropAction) {
863 XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
864
865 if (xclient.get_message_type() !=
866 XDnDConstants.XA_XdndDrop.getAtom()) {
867 return false;
868 }
869
870 /*
871 * The XDnD protocol recommends that the target requests the special
872 * target DELETE in case if the drop action is XdndActionMove.
873 */
874 if (dropAction == DnDConstants.ACTION_MOVE && success) {
875
876 long time_stamp = xclient.get_data(2);
877 long xdndSelectionAtom =
878 XDnDConstants.XDnDSelection.getSelectionAtom().getAtom();
879
880 XToolkit.awtLock();
881 try {
882 XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
883 xdndSelectionAtom,
884 XAtom.get("DELETE").getAtom(),
885 XAtom.get("XAWT_SELECTION").getAtom(),
886 XWindow.getXAWTRootWindow().getWindow(),
887 time_stamp);
888 } finally {
889 XToolkit.awtUnlock();
890 }
891 }
892
893 XClientMessageEvent msg = new XClientMessageEvent();
894 try {
895 msg.set_type((int)XlibWrapper.ClientMessage);
896 msg.set_window(xclient.get_data(0));
897 msg.set_format(32);
898 msg.set_message_type(XDnDConstants.XA_XdndFinished.getAtom());
899 msg.set_data(0, xclient.get_window()); /* target window */
900 msg.set_data(1, 0); /* flags */
901 /* specify an empty rectangle */
902 msg.set_data(2, 0);
903 if (sourceProtocolVersion >= 5) {
904 if (success) {
905 msg.set_data(1, XDnDConstants.XDND_ACCEPT_DROP_FLAG);
906 }
907 /* action performed by the target */
908 msg.set_data(2, XDnDConstants.getXDnDActionForJavaAction(dropAction));
909 }
910 msg.set_data(3, 0);
911 msg.set_data(4, 0);
912
913 XToolkit.awtLock();
914 try {
915 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
916 xclient.get_data(0),
917 false, XlibWrapper.NoEventMask,
918 msg.pData);
919 } finally {
920 XToolkit.awtUnlock();
921 }
922 } finally {
923 msg.dispose();
924 }
925
926 /*
927 * Flush the buffer to guarantee that the drop completion event is sent
928 * to the source before the method returns.
929 */
930 XToolkit.awtLock();
931 try {
932 XlibWrapper.XFlush(XToolkit.getDisplay());
933 } finally {
934 XToolkit.awtUnlock();
935 }
936
937 /* Trick to prevent cleanup() from posting dragExit */
938 targetXWindow = null;
939
940 /* Cannot do cleanup before the drop finishes as we may need
941 source protocol version to send drop finished message. */
942 cleanup();
943 return true;
944 }
945
946 public final long getSourceWindow() {
947 return sourceWindow;
948 }
949
950 /**
951 * Reset the state of the object.
952 */
953 public void cleanup() {
954 // Clear the reference to this protocol.
955 XDropTargetEventProcessor.reset();
956
957 if (targetXWindow != null) {
958 notifyProtocolListener(targetXWindow, 0, 0,
959 DnDConstants.ACTION_NONE, null,
960 MouseEvent.MOUSE_EXITED);
961 }
962
963 if (sourceWindow != 0) {
964 XToolkit.awtLock();
965 try {
966 XToolkit.WITH_XERROR_HANDLER(XToolkit.IgnoreBadWindowHandler);
967 XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow,
968 sourceWindowMask);
969 XToolkit.RESTORE_XERROR_HANDLER();
970 } finally {
971 XToolkit.awtUnlock();
972 }
973 }
974
975 sourceWindow = 0;
976 sourceWindowMask = 0;
977 sourceProtocolVersion = 0;
978 sourceActions = DnDConstants.ACTION_NONE;
979 sourceFormats = null;
980 trackSourceActions = false;
981 userAction = DnDConstants.ACTION_NONE;
982 sourceX = 0;
983 sourceY = 0;
984 targetXWindow = null;
985 }
986
987 public boolean isDragOverComponent() {
988 return targetXWindow != null;
989 }
990
991 public void adjustEventForForwarding(XClientMessageEvent xclient,
992 EmbedderRegistryEntry entry) {
993 /* Adjust the event to match the XDnD protocol version. */
994 int version = entry.getVersion();
995 if (xclient.get_message_type() == XDnDConstants.XA_XdndEnter.getAtom()) {
996 int min_version = sourceProtocolVersion < version ?
997 sourceProtocolVersion : version;
998 long data1 = min_version << XDnDConstants.XDND_PROTOCOL_SHIFT;
999 if (sourceFormats != null && sourceFormats.length > 3) {
1000 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
1001 }
1002 if (logger.isLoggable(Level.FINEST)) {
1003 logger.finest(" "
1004 + " entryVersion=" + version
1005 + " sourceProtocolVersion=" +
1006 sourceProtocolVersion
1007 + " sourceFormats.length=" +
1008 (sourceFormats != null ? sourceFormats.length : 0));
1009 }
1010 xclient.set_data(1, data1);
1011 }
1012 }
1013
1014 private void notifyProtocolListener(XWindow xwindow, int x, int y,
1015 int dropAction,
1016 XClientMessageEvent xclient,
1017 int eventID) {
1018 long nativeCtxt = 0;
1019
1020 // Make a copy of the passed XClientMessageEvent structure, since
1021 // the original structure can be freed before this
1022 // SunDropTargetEvent is dispatched.
1023 if (xclient != null) {
1024 int size = new XClientMessageEvent(nativeCtxt).getSize();
1025
1026 nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1027
1028 unsafe.copyMemory(xclient.pData, nativeCtxt, size);
1029
1030 long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT;
1031 if (sourceFormats != null && sourceFormats.length > 3) {
1032 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
1033 }
1034 // Append information from the latest XdndEnter event.
1035 Native.putLong(nativeCtxt + size, data1);
1036 Native.putLong(nativeCtxt + size + Native.getLongSize(),
1037 sourceFormats.length > 0 ? sourceFormats[0] : 0);
1038 Native.putLong(nativeCtxt + size + 2 * Native.getLongSize(),
1039 sourceFormats.length > 1 ? sourceFormats[1] : 0);
1040 Native.putLong(nativeCtxt + size + 3 * Native.getLongSize(),
1041 sourceFormats.length > 2 ? sourceFormats[2] : 0);
1042 }
1043
1044 getProtocolListener().handleDropTargetNotification(xwindow, x, y,
1045 dropAction,
1046 sourceActions,
1047 sourceFormats,
1048 nativeCtxt,
1049 eventID);
1050 }
1051
1052 /*
1053 * The methods/fields defined below are executed/accessed only on
1054 * the toolkit thread.
1055 * The methods/fields defined below are executed/accessed only on the event
1056 * dispatch thread.
1057 */
1058
1059 public boolean forwardEventToEmbedded(long embedded, long ctxt,
1060 int eventID) {
1061 if (logger.isLoggable(Level.FINEST)) {
1062 logger.finest(" ctxt=" + ctxt +
1063 " type=" + (ctxt != 0 ?
1064 getMessageType(new
1065 XClientMessageEvent(ctxt)) : 0) +
1066 " prevCtxt=" + prevCtxt +
1067 " prevType=" + (prevCtxt != 0 ?
1068 getMessageType(new
1069 XClientMessageEvent(prevCtxt)) : 0));
1070 }
1071 if ((ctxt == 0 ||
1072 getMessageType(new XClientMessageEvent(ctxt)) == UNKNOWN_MESSAGE) &&
1073 (prevCtxt == 0 ||
1074 getMessageType(new XClientMessageEvent(prevCtxt)) == UNKNOWN_MESSAGE)) {
1075 return false;
1076 }
1077
1078 // The size of XClientMessageEvent structure.
1079 int size = XClientMessageEvent.getSize();
1080
1081 if (ctxt != 0) {
1082 XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
1083 if (!overXEmbedClient) {
1084 long data1 = Native.getLong(ctxt + size);
1085 long data2 = Native.getLong(ctxt + size + Native.getLongSize());
1086 long data3 = Native.getLong(ctxt + size + 2 * Native.getLongSize());
1087 long data4 = Native.getLong(ctxt + size + 3 * Native.getLongSize());
1088
1089 if (logger.isLoggable(Level.FINEST)) {
1090 logger.finest(" 1 "
1091 + " embedded=" + embedded
1092 + " source=" + xclient.get_data(0)
1093 + " data1=" + data1
1094 + " data2=" + data2
1095 + " data3=" + data3
1096 + " data4=" + data4);
1097 }
1098
1099 // Copy XdndTypeList from source to proxy.
1100 if ((data1 & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) {
1101 WindowPropertyGetter wpg =
1102 new WindowPropertyGetter(xclient.get_data(0),
1103 XDnDConstants.XA_XdndTypeList,
1104 0, 0xFFFF, false,
1105 XAtom.XA_ATOM);
1106 try {
1107 wpg.execute(XToolkit.IgnoreBadWindowHandler);
1108
1109 if (wpg.getActualType() == XAtom.XA_ATOM &&
1110 wpg.getActualFormat() == 32) {
1111
1112 XToolkit.awtLock();
1113 try {
1114 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
1115 XDnDConstants.XA_XdndTypeList.setAtomData(xclient.get_window(),
1116 XAtom.XA_ATOM,
1117 wpg.getData(),
1118 wpg.getNumberOfItems());
1119 XToolkit.RESTORE_XERROR_HANDLER();
1120
1121 if (XToolkit.saved_error != null &&
1122 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
1123 if (logger.isLoggable(Level.WARNING)) {
1124 logger.warning("Cannot set XdndTypeList on the proxy window");
1125 }
1126 }
1127 } finally {
1128 XToolkit.awtUnlock();
1129 }
1130 } else {
1131 if (logger.isLoggable(Level.WARNING)) {
1132 logger.warning("Cannot read XdndTypeList from the source window");
1133 }
1134 }
1135 } finally {
1136 wpg.dispose();
1137 }
1138 }
1139 XDragSourceContextPeer.setProxyModeSourceWindow(xclient.get_data(0));
1140
1141 sendEnterMessageToToplevelImpl(embedded, xclient.get_window(),
1142 data1, data2, data3, data4);
1143 overXEmbedClient = true;
1144 }
1145
1146 if (logger.isLoggable(Level.FINEST)) {
1147 logger.finest(" 2 "
1148 + " embedded=" + embedded
1149 + " xclient=" + xclient);
1150 }
1151
1152 /* Make a copy of the original event, since we are going to modify the
1153 event while it still can be referenced from other Java events. */
1154 {
1155 XClientMessageEvent copy = new XClientMessageEvent();
1156 unsafe.copyMemory(xclient.pData, copy.pData, copy.getSize());
1157
1158 copy.set_data(0, xclient.get_window());
1159
1160 forwardClientMessageToToplevel(embedded, copy);
1161 }
1162 }
1163
1164 if (eventID == MouseEvent.MOUSE_EXITED) {
1165 if (overXEmbedClient) {
1166 if (ctxt != 0 || prevCtxt != 0) {
1167 // Last chance to send XdndLeave to the XEmbed client.
1168 XClientMessageEvent xclient = ctxt != 0 ?
1169 new XClientMessageEvent(ctxt) :
1170 new XClientMessageEvent(prevCtxt);
1171 sendLeaveMessageToToplevelImpl(embedded, xclient.get_window());
1172 }
1173 overXEmbedClient = false;
1174 // We have to clear the proxy mode source window here,
1175 // when the drag exits the XEmbedCanvasPeer.
1176 // NOTE: at this point the XEmbed client still might have some
1177 // drag notifications to process and it will send responses to
1178 // us. With the proxy mode source window cleared we won't be
1179 // able to forward these responses to the actual source. This is
1180 // not a problem if the drag operation was initiated in this
1181 // JVM. However, if it was initiated in another processes the
1182 // responses will be lost. We bear with it for now, as it seems
1183 // there is no other reliable point to clear.
1184 XDragSourceContextPeer.setProxyModeSourceWindow(0);
1185 }
1186 }
1187
1188 if (eventID == MouseEvent.MOUSE_RELEASED) {
1189 overXEmbedClient = false;
1190 cleanup();
1191 }
1192
1193 if (prevCtxt != 0) {
1194 unsafe.freeMemory(prevCtxt);
1195 prevCtxt = 0;
1196 }
1197
1198 if (ctxt != 0 && overXEmbedClient) {
1199 prevCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1200
1201 unsafe.copyMemory(ctxt, prevCtxt, size + 4 * Native.getLongSize());
1202 }
1203
1204 return true;
1205 }
1206
1207 public boolean isXEmbedSupported() {
1208 return true;
1209 }
1210}