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 
  26 package sun.awt.X11;
  27 
  28 import java.awt.Component;
  29 import java.awt.peer.ComponentPeer;
  30 
  31 import java.io.IOException;
  32 
  33 import java.util.Iterator;
  34 
  35 import sun.awt.AWTAccessor;
  36 import sun.util.logging.PlatformLogger;
  37 
  38 import sun.awt.AppContext;
  39 import sun.awt.SunToolkit;
  40 
  41 import sun.awt.dnd.SunDropTargetContextPeer;
  42 import sun.awt.dnd.SunDropTargetEvent;
  43 
  44 import 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  */
  52 final 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 }