1/*
2 * Copyright (c) 2003, 2015, 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
26package sun.awt.X11;
27
28import java.awt.Component;
29import java.awt.peer.ComponentPeer;
30
31import java.io.IOException;
32
33import java.util.Iterator;
34
35import sun.awt.AWTAccessor;
36import sun.util.logging.PlatformLogger;
37
38import sun.awt.AppContext;
39import sun.awt.SunToolkit;
40
41import sun.awt.dnd.SunDropTargetContextPeer;
42import sun.awt.dnd.SunDropTargetEvent;
43
44import jdk.internal.misc.Unsafe;
45
46/**
47 * The XDropTargetContextPeer is the class responsible for handling
48 * the interaction between the XDnD/Motif DnD subsystem and Java drop targets.
49 *
50 * @since 1.5
51 */
52final class XDropTargetContextPeer extends SunDropTargetContextPeer {
53    private static final PlatformLogger logger =
54        PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDropTargetContextPeer");
55
56    private static final Unsafe unsafe = XlibWrapper.unsafe;
57
58    /*
59     * A key to store a peer instance for an AppContext.
60     */
61    private static final Object DTCP_KEY = "DropTargetContextPeer";
62
63    private XDropTargetContextPeer() {}
64
65    static XDropTargetContextPeer getPeer(AppContext appContext) {
66        synchronized (_globalLock) {
67            XDropTargetContextPeer peer =
68                (XDropTargetContextPeer)appContext.get(DTCP_KEY);
69            if (peer == null) {
70                peer = new XDropTargetContextPeer();
71                appContext.put(DTCP_KEY, peer);
72            }
73
74            return peer;
75        }
76    }
77
78    static XDropTargetProtocolListener getXDropTargetProtocolListener() {
79        return XDropTargetProtocolListenerImpl.getInstance();
80    }
81
82    /*
83     * @param returnValue the drop action selected by the Java drop target.
84     */
85    protected void eventProcessed(SunDropTargetEvent e, int returnValue,
86                                  boolean dispatcherDone) {
87        /* The native context is the pointer to the XClientMessageEvent
88           structure. */
89        long ctxt = getNativeDragContext();
90        /* If the event was not consumed, send a response to the source. */
91        try {
92            if (ctxt != 0 && !e.isConsumed()) {
93                Iterator<XDropTargetProtocol> dropTargetProtocols =
94                    XDragAndDropProtocols.getDropTargetProtocols();
95
96                while (dropTargetProtocols.hasNext()) {
97                    XDropTargetProtocol dropTargetProtocol =
98                        dropTargetProtocols.next();
99                    if (dropTargetProtocol.sendResponse(ctxt, e.getID(),
100                                                        returnValue)) {
101                        break;
102                    }
103                }
104            }
105        } finally {
106            if (dispatcherDone && ctxt != 0) {
107                unsafe.freeMemory(ctxt);
108            }
109        }
110    }
111
112    protected void doDropDone(boolean success, int dropAction,
113                              boolean isLocal) {
114        /* The native context is the pointer to the XClientMessageEvent
115           structure. */
116        long ctxt = getNativeDragContext();
117
118        if (ctxt != 0) {
119            try {
120                Iterator<XDropTargetProtocol> dropTargetProtocols =
121                    XDragAndDropProtocols.getDropTargetProtocols();
122
123                while (dropTargetProtocols.hasNext()) {
124                    XDropTargetProtocol dropTargetProtocol =
125                        dropTargetProtocols.next();
126                    if (dropTargetProtocol.sendDropDone(ctxt, success,
127                                                        dropAction)) {
128                        break;
129                    }
130                }
131            } finally {
132                unsafe.freeMemory(ctxt);
133            }
134        }
135    }
136
137    protected Object getNativeData(long format)
138      throws IOException {
139        /* The native context is the pointer to the XClientMessageEvent
140           structure. */
141        long ctxt = getNativeDragContext();
142
143        if (ctxt != 0) {
144            Iterator<XDropTargetProtocol> dropTargetProtocols =
145                XDragAndDropProtocols.getDropTargetProtocols();
146
147            while (dropTargetProtocols.hasNext()) {
148                XDropTargetProtocol dropTargetProtocol =
149                    dropTargetProtocols.next();
150                // getData throws IAE if ctxt is not for this protocol.
151                try {
152                    return dropTargetProtocol.getData(ctxt, format);
153                } catch (IllegalArgumentException iae) {
154                }
155            }
156        }
157
158        return null;
159    }
160
161    private void cleanup() {
162    }
163
164    protected void processEnterMessage(SunDropTargetEvent event) {
165        if (!processSunDropTargetEvent(event)) {
166            super.processEnterMessage(event);
167        }
168    }
169
170    protected void processExitMessage(SunDropTargetEvent event) {
171        if (!processSunDropTargetEvent(event)) {
172            super.processExitMessage(event);
173        }
174    }
175
176    protected void processMotionMessage(SunDropTargetEvent event,
177                                        boolean operationChanged) {
178        if (!processSunDropTargetEvent(event)) {
179            super.processMotionMessage(event, operationChanged);
180        }
181    }
182
183    protected void processDropMessage(SunDropTargetEvent event) {
184        if (!processSunDropTargetEvent(event)) {
185            super.processDropMessage(event);
186        }
187    }
188
189    // If source is an XEmbedCanvasPeer, passes the event to it for processing and
190    // return true if the event is forwarded to the XEmbed child.
191    // Otherwise, does nothing and return false.
192    private boolean processSunDropTargetEvent(SunDropTargetEvent event) {
193        Object source = event.getSource();
194
195        if (source instanceof Component) {
196            Object peer = AWTAccessor.getComponentAccessor()
197                                     .getPeer((Component) source);
198            if (peer instanceof XEmbedCanvasPeer) {
199                XEmbedCanvasPeer xEmbedCanvasPeer = (XEmbedCanvasPeer)peer;
200                /* The native context is the pointer to the XClientMessageEvent
201                   structure. */
202                long ctxt = getNativeDragContext();
203
204                if (logger.isLoggable(PlatformLogger.Level.FINER)) {
205                    logger.finer("        processing " + event + " ctxt=" + ctxt +
206                                 " consumed=" + event.isConsumed());
207                }
208                /* If the event is not consumed, pass it to the
209                   XEmbedCanvasPeer for processing. */
210                if (!event.isConsumed()) {
211                    // NOTE: ctxt can be zero at this point.
212                    if (xEmbedCanvasPeer.processXEmbedDnDEvent(ctxt,
213                                                               event.getID())) {
214                        event.consume();
215                        return true;
216                    }
217                }
218            }
219        }
220
221        return false;
222    }
223
224    public void forwardEventToEmbedded(long embedded, long ctxt,
225                                       int eventID) {
226        Iterator<XDropTargetProtocol> dropTargetProtocols =
227            XDragAndDropProtocols.getDropTargetProtocols();
228
229        while (dropTargetProtocols.hasNext()) {
230            XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
231            if (dropTargetProtocol.forwardEventToEmbedded(embedded, ctxt,
232                                                          eventID)) {
233                break;
234            }
235        }
236    }
237
238    static final class XDropTargetProtocolListenerImpl
239        implements XDropTargetProtocolListener {
240
241        private static final XDropTargetProtocolListener theInstance =
242            new XDropTargetProtocolListenerImpl();
243
244        private XDropTargetProtocolListenerImpl() {}
245
246        static XDropTargetProtocolListener getInstance() {
247            return theInstance;
248        }
249
250        public void handleDropTargetNotification(XWindow xwindow, int x, int y,
251                                                 int dropAction, int actions,
252                                                 long[] formats, long nativeCtxt,
253                                                 int eventID) {
254            Object target = xwindow.getTarget();
255
256            // The Every component is associated with some AppContext.
257            assert target instanceof Component;
258
259            Component component = (Component)target;
260
261            AppContext appContext = SunToolkit.targetToAppContext(target);
262
263            // Every component is associated with some AppContext.
264            assert appContext != null;
265
266            XDropTargetContextPeer peer = XDropTargetContextPeer.getPeer(appContext);
267
268            peer.postDropTargetEvent(component, x, y, dropAction, actions, formats,
269                                     nativeCtxt, eventID,
270                                     !SunDropTargetContextPeer.DISPATCH_SYNC);
271        }
272    }
273}
274