1 /*
2  * Copyright (c) 2003, 2013, 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.datatransfer.Transferable;
29 import java.awt.datatransfer.DataFlavor;
30 
31 import java.awt.dnd.DnDConstants;
32 import java.awt.dnd.InvalidDnDOperationException;
33 
34 import java.util.Map;
35 
36 /**
37  * An abstract class for drag protocols on X11 systems.
38  * Contains protocol-independent drag source code.
39  *
40  * @since 1.5
41  */
42 abstract class XDragSourceProtocol {
43     private final XDragSourceProtocolListener listener;
44 
45     private boolean initialized = false;
46 
47     private long targetWindow = 0;
48     private long targetProxyWindow = 0;
49     private int targetProtocolVersion = 0;
50     private long targetWindowMask = 0;
51 
52     // Always use the XAWT root window as the drag source window.
getDragSourceWindow()53     static long getDragSourceWindow() {
54         return XWindow.getXAWTRootWindow().getWindow();
55     }
56 
XDragSourceProtocol(XDragSourceProtocolListener listener)57     protected XDragSourceProtocol(XDragSourceProtocolListener listener) {
58         if (listener == null) {
59             throw new NullPointerException("Null XDragSourceProtocolListener");
60         }
61         this.listener = listener;
62     }
63 
getProtocolListener()64     protected final XDragSourceProtocolListener getProtocolListener() {
65         return listener;
66     }
67 
68     /**
69      * Returns the protocol name. The protocol name cannot be null.
70      */
getProtocolName()71     public abstract String getProtocolName();
72 
73     /**
74      * Initializes a drag operation with the specified supported drop actions,
75      * contents and data formats.
76      *
77      * @param actions a bitwise mask of {@code DnDConstants} that represent
78      *                the supported drop actions.
79      * @param contents the contents for the drag operation.
80      * @param formats an array of Atoms that represent the supported data formats.
81      * @param formats an array of Atoms that represent the supported data formats.
82      * @throws InvalidDnDOperationException if a drag operation is already
83      * initialized.
84      * @throws IllegalArgumentException if some argument has invalid value.
85      * @throws XException if some X call failed.
86      */
initializeDrag(int actions, Transferable contents, Map<Long, DataFlavor> formatMap, long[] formats)87     public final void initializeDrag(int actions, Transferable contents,
88                                      Map<Long, DataFlavor> formatMap, long[] formats)
89       throws InvalidDnDOperationException,
90              IllegalArgumentException, XException {
91         XToolkit.awtLock();
92         try {
93             try {
94                 if (initialized) {
95                     throw new InvalidDnDOperationException("Already initialized");
96                 }
97 
98                 initializeDragImpl(actions, contents, formatMap, formats);
99 
100                 initialized = true;
101             } finally {
102                 if (!initialized) {
103                     cleanup();
104                 }
105             }
106         } finally {
107             XToolkit.awtUnlock();
108         }
109     }
110 
111     /* The caller must hold AWT_LOCK. */
initializeDragImpl(int actions, Transferable contents, Map<Long, DataFlavor> formatMap, long[] formats)112     protected abstract void initializeDragImpl(int actions,
113                                                Transferable contents,
114                                                Map<Long, DataFlavor> formatMap,
115                                                long[] formats)
116       throws InvalidDnDOperationException, IllegalArgumentException, XException;
117 
118     /**
119      * Terminates the current drag operation (if any) and resets the internal
120      * state of this object.
121      *
122      * @throws XException if some X call failed.
123      */
cleanup()124     public void cleanup() {
125         initialized = false;
126         cleanupTargetInfo();
127     }
128 
129     /**
130      * Clears the information on the current drop target.
131      *
132      * @throws XException if some X call failed.
133      */
cleanupTargetInfo()134     public void cleanupTargetInfo() {
135         targetWindow = 0;
136         targetProxyWindow = 0;
137         targetProtocolVersion = 0;
138     }
139 
140     /**
141      * Processes the specified client message event.
142      *
143      * @return true if the event was successfully processed.
144      */
processClientMessage(XClientMessageEvent xclient)145     public abstract boolean processClientMessage(XClientMessageEvent xclient)
146       throws XException;
147 
148     /* The caller must hold AWT_LOCK. */
attachTargetWindow(long window, long time)149     public final boolean attachTargetWindow(long window, long time) {
150         assert XToolkit.isAWTLockHeldByCurrentThread();
151 
152         TargetWindowInfo info = getTargetWindowInfo(window);
153         if (info == null) {
154             return false;
155         } else {
156             targetWindow = window;
157             targetProxyWindow = info.getProxyWindow();
158             targetProtocolVersion = info.getProtocolVersion();
159             return true;
160         }
161     }
162 
163     /* The caller must hold AWT_LOCK. */
getTargetWindowInfo(long window)164     public abstract TargetWindowInfo getTargetWindowInfo(long window);
165 
166     /* The caller must hold AWT_LOCK. */
sendEnterMessage(long[] formats, int sourceAction, int sourceActions, long time)167     public abstract void sendEnterMessage(long[] formats, int sourceAction,
168                                           int sourceActions, long time);
169     /* The caller must hold AWT_LOCK. */
sendMoveMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)170     public abstract void sendMoveMessage(int xRoot, int yRoot,
171                                          int sourceAction, int sourceActions,
172                                          long time);
173     /* The caller must hold AWT_LOCK. */
sendLeaveMessage(long time)174     public abstract void sendLeaveMessage(long time);
175 
176     /* The caller must hold AWT_LOCK. */
sendDropMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)177     protected abstract void sendDropMessage(int xRoot, int yRoot,
178                                             int sourceAction, int sourceActions,
179                                             long time);
180 
initiateDrop(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)181     public final void initiateDrop(int xRoot, int yRoot,
182                                    int sourceAction, int sourceActions,
183                                    long time) {
184         XWindowAttributes wattr = new XWindowAttributes();
185         try {
186             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
187             int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
188                                                           targetWindow, wattr.pData);
189 
190             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
191 
192             if ((status == 0) ||
193                 ((XErrorHandlerUtil.saved_error != null) &&
194                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
195                 throw new XException("XGetWindowAttributes failed");
196             }
197 
198             targetWindowMask = wattr.get_your_event_mask();
199         } finally {
200             wattr.dispose();
201         }
202 
203         XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
204         XlibWrapper.XSelectInput(XToolkit.getDisplay(), targetWindow,
205                                  targetWindowMask |
206                                  XConstants.StructureNotifyMask);
207 
208         XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
209 
210         if ((XErrorHandlerUtil.saved_error != null) &&
211             (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
212             throw new XException("XSelectInput failed");
213         }
214 
215         sendDropMessage(xRoot, yRoot, sourceAction, sourceActions, time);
216     }
217 
finalizeDrop()218     protected final void finalizeDrop() {
219         XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
220         XlibWrapper.XSelectInput(XToolkit.getDisplay(), targetWindow,
221                                  targetWindowMask);
222         XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
223     }
224 
processProxyModeEvent(XClientMessageEvent xclient, long sourceWindow)225     public abstract boolean processProxyModeEvent(XClientMessageEvent xclient,
226                                                   long sourceWindow);
227 
getTargetWindow()228     protected final long getTargetWindow() {
229         return targetWindow;
230     }
231 
getTargetProxyWindow()232     protected final long getTargetProxyWindow() {
233         if (targetProxyWindow != 0) {
234             return targetProxyWindow;
235         } else {
236             return targetWindow;
237         }
238     }
239 
getTargetProtocolVersion()240     protected final int getTargetProtocolVersion() {
241         return targetProtocolVersion;
242     }
243 
244     public static class TargetWindowInfo {
245         private final long proxyWindow;
246         private final int protocolVersion;
TargetWindowInfo(long proxy, int version)247         public TargetWindowInfo(long proxy, int version) {
248             proxyWindow = proxy;
249             protocolVersion = version;
250         }
getProxyWindow()251         public long getProxyWindow() {
252             return proxyWindow;
253         }
getProtocolVersion()254         public int getProtocolVersion() {
255             return protocolVersion;
256         }
257     }
258 }
259