001: /*
002: * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.X11;
027:
028: import java.io.IOException;
029:
030: import java.util.HashMap;
031:
032: import java.util.logging.*;
033:
034: /**
035: * An abstract class for drop protocols on X11 systems.
036: * Contains protocol-independent drop target code.
037: *
038: * @since 1.5
039: */
040: abstract class XDropTargetProtocol {
041: private static final Logger logger = Logger
042: .getLogger("sun.awt.X11.xembed.xdnd.XDropTargetProtocol");
043:
044: private final XDropTargetProtocolListener listener;
045:
046: public static final int EMBEDDER_ALREADY_REGISTERED = 0;
047:
048: public static final int UNKNOWN_MESSAGE = 0;
049: public static final int ENTER_MESSAGE = 1;
050: public static final int MOTION_MESSAGE = 2;
051: public static final int LEAVE_MESSAGE = 3;
052: public static final int DROP_MESSAGE = 4;
053:
054: protected XDropTargetProtocol(XDropTargetProtocolListener listener) {
055: if (listener == null) {
056: throw new NullPointerException(
057: "Null XDropTargetProtocolListener");
058: }
059: this .listener = listener;
060: }
061:
062: protected final XDropTargetProtocolListener getProtocolListener() {
063: return listener;
064: }
065:
066: /**
067: * Returns the protocol name. The protocol name cannot be null.
068: */
069: public abstract String getProtocolName();
070:
071: /* The caller must hold AWT_LOCK. */
072: public abstract void registerDropTarget(long window);
073:
074: /* The caller must hold AWT_LOCK. */
075: public abstract void unregisterDropTarget(long window);
076:
077: /* The caller must hold AWT_LOCK. */
078: public abstract void registerEmbedderDropSite(long window);
079:
080: /* The caller must hold AWT_LOCK. */
081: public abstract void unregisterEmbedderDropSite(long window);
082:
083: /* The caller must hold AWT_LOCK. */
084: public abstract void registerEmbeddedDropSite(long embedded);
085:
086: /* The caller must hold AWT_LOCK. */
087: public final void unregisterEmbeddedDropSite(long embedded) {
088: removeEmbedderRegistryEntry(embedded);
089: }
090:
091: /* The caller must hold AWT_LOCK. */
092: public abstract boolean isProtocolSupported(long window);
093:
094: public abstract int getMessageType(XClientMessageEvent xclient);
095:
096: /* The caller must hold AWT_LOCK. */
097: public final boolean processClientMessage(
098: XClientMessageEvent xclient) {
099: int type = getMessageType(xclient);
100: boolean processed = processClientMessageImpl(xclient);
101:
102: postProcessClientMessage(xclient, processed, type);
103:
104: return processed;
105: }
106:
107: /* The caller must hold AWT_LOCK. */
108: protected abstract boolean processClientMessageImpl(
109: XClientMessageEvent xclient);
110:
111: /*
112: * Forwards a drag notification to the embedding toplevel modifying the event
113: * to match the protocol version supported by the toplevel.
114: * The caller must hold AWT_LOCK.
115: * Returns True if the event is sent, False otherwise.
116: */
117: protected final boolean forwardClientMessageToToplevel(
118: long toplevel, XClientMessageEvent xclient) {
119: EmbedderRegistryEntry entry = getEmbedderRegistryEntry(toplevel);
120:
121: if (logger.isLoggable(Level.FINEST)) {
122: logger.log(Level.FINEST, " entry={0}",
123: new Object[] { entry });
124: }
125: // Window not registered as an embedder for this protocol.
126: if (entry == null) {
127: return false;
128: }
129:
130: if (logger.isLoggable(Level.FINEST)) {
131: logger.log(Level.FINEST, " entry.isOverriden()={0}",
132: new Object[] { entry.isOverriden() });
133: }
134: // Window didn't have an associated drop site, so there is no need
135: // to forward the message.
136: if (!entry.isOverriden()) {
137: return false;
138: }
139:
140: adjustEventForForwarding(xclient, entry);
141:
142: long proxy = entry.getProxy();
143:
144: if (logger.isLoggable(Level.FINEST)) {
145: logger.log(Level.FINEST, " proxy={0} toplevel={1}",
146: new Object[] { proxy, toplevel });
147: }
148: if (proxy == 0) {
149: proxy = toplevel;
150: }
151:
152: xclient.set_window(toplevel);
153:
154: XToolkit.awtLock();
155: try {
156: XlibWrapper.XSendEvent(XToolkit.getDisplay(), proxy, false,
157: XlibWrapper.NoEventMask, xclient.pData);
158: } finally {
159: XToolkit.awtUnlock();
160: }
161:
162: return true;
163: }
164:
165: /* True iff the previous notification was MotionEvent and it was
166: forwarded to the browser. */
167: private boolean motionPassedAlong = false;
168:
169: protected abstract void sendEnterMessageToToplevel(long toplevel,
170: XClientMessageEvent xclient);
171:
172: protected abstract void sendLeaveMessageToToplevel(long toplevel,
173: XClientMessageEvent xclient);
174:
175: private void postProcessClientMessage(XClientMessageEvent xclient,
176: boolean processed, int type) {
177: long toplevel = xclient.get_window();
178:
179: if (getEmbedderRegistryEntry(toplevel) != null) {
180: /*
181: * This code forwards drag notifications to the browser according to the
182: * following rules:
183: * - the messages that we failed to process are always forwarded to the
184: * browser;
185: * - MotionEvents and DropEvents are forwarded if and only if the drag
186: * is not over a plugin window;
187: * - XDnD: EnterEvents and LeaveEvents are never forwarded, instead, we
188: * send synthesized EnterEvents or LeaveEvents when the drag
189: * respectively exits or enters plugin windows;
190: * - Motif DnD: EnterEvents and LeaveEvents are always forwarded.
191: * Synthetic EnterEvents and LeaveEvents are needed, because the XDnD drop
192: * site implemented Netscape 6.2 has a nice feature: when it receives
193: * the first XdndPosition it continuously sends XdndStatus messages to
194: * the source (every 100ms) until the drag terminates or leaves the drop
195: * site. When the mouse is dragged over plugin window embedded in the
196: * browser frame, these XdndStatus messages are mixed with the XdndStatus
197: * messages sent from the plugin.
198: * For Motif DnD, synthetic events cause Motif warnings being displayed,
199: * so these events are always forwarded. However, Motif DnD drop site in
200: * Netscape 6.2 is implemented in the same way, so there could be similar
201: * problems if the drag source choose Motif DnD for communication.
202: */
203: if (!processed) {
204: forwardClientMessageToToplevel(toplevel, xclient);
205: } else {
206: boolean motifProtocol = xclient.get_message_type() == MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE
207: .getAtom();
208:
209: switch (type) {
210: case XDropTargetProtocol.MOTION_MESSAGE:
211: if (!isDragOverComponent()) {
212: if (!motionPassedAlong && !motifProtocol) {
213: sendEnterMessageToToplevel(toplevel,
214: xclient);
215: }
216: forwardClientMessageToToplevel(toplevel,
217: xclient);
218: motionPassedAlong = true;
219: } else {
220: if (motionPassedAlong && !motifProtocol) {
221: sendLeaveMessageToToplevel(toplevel,
222: xclient);
223: }
224: motionPassedAlong = false;
225: }
226: break;
227: case XDropTargetProtocol.DROP_MESSAGE:
228: if (!isDragOverComponent()) {
229: forwardClientMessageToToplevel(toplevel,
230: xclient);
231: }
232: motionPassedAlong = false;
233: break;
234: case XDropTargetProtocol.ENTER_MESSAGE:
235: case XDropTargetProtocol.LEAVE_MESSAGE:
236: if (motifProtocol) {
237: forwardClientMessageToToplevel(toplevel,
238: xclient);
239: }
240: motionPassedAlong = false;
241: break;
242: }
243: }
244: }
245: }
246:
247: public abstract boolean sendResponse(long ctxt, int eventID,
248: int action);
249:
250: /*
251: * Retrieves the data from the drag source in the specified format.
252: *
253: * @param ctxt a pointer to the XClientMessageEvent structure for this
254: * protocol's drop message.
255: * @param format the format in which the data should be retrieved.
256: *
257: * @throws IllegalArgumentException if ctxt doesn't point to the
258: * XClientMessageEvent structure for this protocol's drop message.
259: * @throws IOException if data retrieval failed.
260: */
261: public abstract Object getData(long ctxt, long format)
262: throws IllegalArgumentException, IOException;
263:
264: public abstract boolean sendDropDone(long ctxt, boolean success,
265: int dropAction);
266:
267: public abstract long getSourceWindow();
268:
269: public abstract void cleanup();
270:
271: public abstract boolean isDragOverComponent();
272:
273: public void adjustEventForForwarding(XClientMessageEvent xclient,
274: EmbedderRegistryEntry entry) {
275: }
276:
277: public abstract boolean forwardEventToEmbedded(long embedded,
278: long ctxt, int eventID);
279:
280: /*
281: * Returns true if the XEmbed protocol prescribes that an XEmbed server must
282: * support this DnD protocol for drop sites associated with XEmbed clients.
283: */
284: public abstract boolean isXEmbedSupported();
285:
286: protected static final class EmbedderRegistryEntry {
287: private final boolean overriden;
288: private final int version;
289: private final long proxy;
290:
291: EmbedderRegistryEntry(boolean overriden, int version, long proxy) {
292: this .overriden = overriden;
293: this .version = version;
294: this .proxy = proxy;
295: }
296:
297: public boolean isOverriden() {
298: return overriden;
299: }
300:
301: public int getVersion() {
302: return version;
303: }
304:
305: public long getProxy() {
306: return proxy;
307: }
308: }
309:
310: /* Access to HashMap is synchronized on this XDropTargetProtocol instance. */
311: private final HashMap embedderRegistry = new HashMap();
312:
313: protected final void putEmbedderRegistryEntry(long embedder,
314: boolean overriden, int version, long proxy) {
315: synchronized (this ) {
316: embedderRegistry
317: .put(Long.valueOf(embedder),
318: new EmbedderRegistryEntry(overriden,
319: version, proxy));
320: }
321: }
322:
323: protected final EmbedderRegistryEntry getEmbedderRegistryEntry(
324: long embedder) {
325: synchronized (this ) {
326: return (EmbedderRegistryEntry) embedderRegistry.get(Long
327: .valueOf(embedder));
328: }
329: }
330:
331: protected final void removeEmbedderRegistryEntry(long embedder) {
332: synchronized (this ) {
333: embedderRegistry.remove(new Long(embedder));
334: }
335: }
336: }
|