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