| /* |
| * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.awt.X11; |
| |
| import java.io.IOException; |
| |
| import java.util.HashMap; |
| |
| import sun.util.logging.PlatformLogger; |
| |
| /** |
| * An abstract class for drop protocols on X11 systems. |
| * Contains protocol-independent drop target code. |
| * |
| * @since 1.5 |
| */ |
| abstract class XDropTargetProtocol { |
| private static final PlatformLogger logger = |
| PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDropTargetProtocol"); |
| |
| private final XDropTargetProtocolListener listener; |
| |
| public static final int EMBEDDER_ALREADY_REGISTERED = 0; |
| |
| public static final int UNKNOWN_MESSAGE = 0; |
| public static final int ENTER_MESSAGE = 1; |
| public static final int MOTION_MESSAGE = 2; |
| public static final int LEAVE_MESSAGE = 3; |
| public static final int DROP_MESSAGE = 4; |
| |
| protected XDropTargetProtocol(XDropTargetProtocolListener listener) { |
| if (listener == null) { |
| throw new NullPointerException("Null XDropTargetProtocolListener"); |
| } |
| this.listener = listener; |
| } |
| |
| protected final XDropTargetProtocolListener getProtocolListener() { |
| return listener; |
| } |
| |
| /** |
| * Returns the protocol name. The protocol name cannot be null. |
| */ |
| public abstract String getProtocolName(); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public abstract void registerDropTarget(long window); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public abstract void unregisterDropTarget(long window); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public abstract void registerEmbedderDropSite(long window); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public abstract void unregisterEmbedderDropSite(long window); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public abstract void registerEmbeddedDropSite(long embedded); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public final void unregisterEmbeddedDropSite(long embedded) { |
| removeEmbedderRegistryEntry(embedded); |
| } |
| |
| |
| /* The caller must hold AWT_LOCK. */ |
| public abstract boolean isProtocolSupported(long window); |
| |
| public abstract int getMessageType(XClientMessageEvent xclient); |
| |
| /* The caller must hold AWT_LOCK. */ |
| public final boolean processClientMessage(XClientMessageEvent xclient) { |
| int type = getMessageType(xclient); |
| boolean processed = processClientMessageImpl(xclient); |
| |
| postProcessClientMessage(xclient, processed, type); |
| |
| return processed; |
| } |
| |
| /* The caller must hold AWT_LOCK. */ |
| protected abstract boolean processClientMessageImpl(XClientMessageEvent xclient); |
| |
| /* |
| * Forwards a drag notification to the embedding toplevel modifying the event |
| * to match the protocol version supported by the toplevel. |
| * The caller must hold AWT_LOCK. |
| * Returns True if the event is sent, False otherwise. |
| */ |
| protected final boolean forwardClientMessageToToplevel(long toplevel, |
| XClientMessageEvent xclient) { |
| EmbedderRegistryEntry entry = getEmbedderRegistryEntry(toplevel); |
| |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" entry={0}", entry); |
| } |
| // Window not registered as an embedder for this protocol. |
| if (entry == null) { |
| return false; |
| } |
| |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" entry.isOverriden()={0}", entry.isOverriden()); |
| } |
| // Window didn't have an associated drop site, so there is no need |
| // to forward the message. |
| if (!entry.isOverriden()) { |
| return false; |
| } |
| |
| adjustEventForForwarding(xclient, entry); |
| |
| long proxy = entry.getProxy(); |
| |
| if (logger.isLoggable(PlatformLogger.FINEST)) { |
| logger.finest(" proxy={0} toplevel={1}", proxy, toplevel); |
| } |
| if (proxy == 0) { |
| proxy = toplevel; |
| } |
| |
| xclient.set_window(toplevel); |
| |
| XToolkit.awtLock(); |
| try { |
| XlibWrapper.XSendEvent(XToolkit.getDisplay(), proxy, false, |
| XConstants.NoEventMask, xclient.pData); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| |
| return true; |
| } |
| |
| |
| /* True iff the previous notification was MotionEvent and it was |
| forwarded to the browser. */ |
| private boolean motionPassedAlong = false; |
| |
| protected abstract void sendEnterMessageToToplevel(long toplevel, |
| XClientMessageEvent xclient); |
| |
| protected abstract void sendLeaveMessageToToplevel(long toplevel, |
| XClientMessageEvent xclient); |
| |
| private void postProcessClientMessage(XClientMessageEvent xclient, |
| boolean processed, |
| int type) { |
| long toplevel = xclient.get_window(); |
| |
| if (getEmbedderRegistryEntry(toplevel) != null) { |
| /* |
| * This code forwards drag notifications to the browser according to the |
| * following rules: |
| * - the messages that we failed to process are always forwarded to the |
| * browser; |
| * - MotionEvents and DropEvents are forwarded if and only if the drag |
| * is not over a plugin window; |
| * - XDnD: EnterEvents and LeaveEvents are never forwarded, instead, we |
| * send synthesized EnterEvents or LeaveEvents when the drag |
| * respectively exits or enters plugin windows; |
| * - Motif DnD: EnterEvents and LeaveEvents are always forwarded. |
| * Synthetic EnterEvents and LeaveEvents are needed, because the XDnD drop |
| * site implemented Netscape 6.2 has a nice feature: when it receives |
| * the first XdndPosition it continuously sends XdndStatus messages to |
| * the source (every 100ms) until the drag terminates or leaves the drop |
| * site. When the mouse is dragged over plugin window embedded in the |
| * browser frame, these XdndStatus messages are mixed with the XdndStatus |
| * messages sent from the plugin. |
| * For Motif DnD, synthetic events cause Motif warnings being displayed, |
| * so these events are always forwarded. However, Motif DnD drop site in |
| * Netscape 6.2 is implemented in the same way, so there could be similar |
| * problems if the drag source choose Motif DnD for communication. |
| */ |
| if (!processed) { |
| forwardClientMessageToToplevel(toplevel, xclient); |
| } else { |
| boolean motifProtocol = |
| xclient.get_message_type() == |
| MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom(); |
| |
| switch (type) { |
| case XDropTargetProtocol.MOTION_MESSAGE: |
| if (!isDragOverComponent()) { |
| if (!motionPassedAlong && !motifProtocol) { |
| sendEnterMessageToToplevel(toplevel, xclient); |
| } |
| forwardClientMessageToToplevel(toplevel, xclient); |
| motionPassedAlong = true; |
| } else { |
| if (motionPassedAlong && !motifProtocol) { |
| sendLeaveMessageToToplevel(toplevel, xclient); |
| } |
| motionPassedAlong = false; |
| } |
| break; |
| case XDropTargetProtocol.DROP_MESSAGE: |
| if (!isDragOverComponent()) { |
| forwardClientMessageToToplevel(toplevel, xclient); |
| } |
| motionPassedAlong = false; |
| break; |
| case XDropTargetProtocol.ENTER_MESSAGE: |
| case XDropTargetProtocol.LEAVE_MESSAGE: |
| if (motifProtocol) { |
| forwardClientMessageToToplevel(toplevel, xclient); |
| } |
| motionPassedAlong = false; |
| break; |
| } |
| } |
| } |
| } |
| |
| public abstract boolean sendResponse(long ctxt, int eventID, int action); |
| |
| /* |
| * Retrieves the data from the drag source in the specified format. |
| * |
| * @param ctxt a pointer to the XClientMessageEvent structure for this |
| * protocol's drop message. |
| * @param format the format in which the data should be retrieved. |
| * |
| * @throws IllegalArgumentException if ctxt doesn't point to the |
| * XClientMessageEvent structure for this protocol's drop message. |
| * @throws IOException if data retrieval failed. |
| */ |
| public abstract Object getData(long ctxt, long format) |
| throws IllegalArgumentException, IOException; |
| |
| public abstract boolean sendDropDone(long ctxt, boolean success, |
| int dropAction); |
| |
| public abstract long getSourceWindow(); |
| |
| public abstract void cleanup(); |
| |
| public abstract boolean isDragOverComponent(); |
| |
| public void adjustEventForForwarding(XClientMessageEvent xclient, |
| EmbedderRegistryEntry entry) {} |
| |
| public abstract boolean forwardEventToEmbedded(long embedded, long ctxt, |
| int eventID); |
| |
| /* |
| * Returns true if the XEmbed protocol prescribes that an XEmbed server must |
| * support this DnD protocol for drop sites associated with XEmbed clients. |
| */ |
| public abstract boolean isXEmbedSupported(); |
| |
| protected static final class EmbedderRegistryEntry { |
| private final boolean overriden; |
| private final int version; |
| private final long proxy; |
| EmbedderRegistryEntry(boolean overriden, int version, long proxy) { |
| this.overriden = overriden; |
| this.version = version; |
| this.proxy = proxy; |
| } |
| public boolean isOverriden() { |
| return overriden; |
| } |
| public int getVersion() { |
| return version; |
| } |
| public long getProxy() { |
| return proxy; |
| } |
| } |
| |
| /* Access to HashMap is synchronized on this XDropTargetProtocol instance. */ |
| private final HashMap embedderRegistry = new HashMap(); |
| |
| protected final void putEmbedderRegistryEntry(long embedder, |
| boolean overriden, |
| int version, |
| long proxy) { |
| synchronized (this) { |
| embedderRegistry.put(Long.valueOf(embedder), |
| new EmbedderRegistryEntry(overriden, version, |
| proxy)); |
| } |
| } |
| |
| protected final EmbedderRegistryEntry getEmbedderRegistryEntry(long embedder) { |
| synchronized (this) { |
| return |
| (EmbedderRegistryEntry)embedderRegistry.get(Long.valueOf(embedder)); |
| } |
| } |
| |
| protected final void removeEmbedderRegistryEntry(long embedder) { |
| synchronized (this) { |
| embedderRegistry.remove(Long.valueOf(embedder)); |
| } |
| } |
| } |