24 */ |
24 */ |
25 |
25 |
26 package sun.awt.X11; |
26 package sun.awt.X11; |
27 |
27 |
28 import java.awt.datatransfer.Transferable; |
28 import java.awt.datatransfer.Transferable; |
29 |
|
30 import java.util.SortedMap; |
29 import java.util.SortedMap; |
31 import java.util.Set; |
|
32 import java.util.Iterator; |
|
33 import java.util.HashSet; |
|
34 |
|
35 import java.io.IOException; |
30 import java.io.IOException; |
36 |
|
37 import java.security.AccessController; |
31 import java.security.AccessController; |
38 |
32 import java.util.HashMap; |
|
33 import java.util.Map; |
|
34 import sun.awt.UNIXToolkit; |
39 import sun.awt.datatransfer.DataTransferer; |
35 import sun.awt.datatransfer.DataTransferer; |
40 import sun.awt.datatransfer.SunClipboard; |
36 import sun.awt.datatransfer.SunClipboard; |
41 import sun.awt.datatransfer.ClipboardTransferable; |
37 import sun.awt.datatransfer.ClipboardTransferable; |
42 |
|
43 import sun.security.action.GetIntegerAction; |
38 import sun.security.action.GetIntegerAction; |
44 |
|
45 |
|
46 |
39 |
47 /** |
40 /** |
48 * A class which interfaces with the X11 selection service in order to support |
41 * A class which interfaces with the X11 selection service in order to support |
49 * data transfer via Clipboard operations. |
42 * data transfer via Clipboard operations. |
50 */ |
43 */ |
51 public class XClipboard extends SunClipboard implements Runnable { |
44 public final class XClipboard extends SunClipboard implements OwnershipListener |
|
45 { |
52 private final XSelection selection; |
46 private final XSelection selection; |
|
47 // Time of calling XConvertSelection(). |
|
48 private long convertSelectionTime; |
|
49 // The flag used not to call XConvertSelection() if the previous SelectionNotify |
|
50 // has not been processed by checkChange(). |
|
51 private volatile boolean isSelectionNotifyProcessed; |
|
52 // The property in which the owner should place requested targets |
|
53 // when tracking changes of available data flavors (practically targets). |
|
54 private volatile XAtom targetsPropertyAtom; |
53 |
55 |
54 private static final Object classLock = new Object(); |
56 private static final Object classLock = new Object(); |
55 |
57 |
56 private static final int defaultPollInterval = 200; |
58 private static final int defaultPollInterval = 200; |
57 |
59 |
58 private static int pollInterval; |
60 private static int pollInterval; |
59 |
61 |
60 private static Set listenedClipboards; |
62 private static Map<Long, XClipboard> targetsAtom2Clipboard; |
61 |
|
62 |
63 |
63 /** |
64 /** |
64 * Creates a system clipboard object. |
65 * Creates a system clipboard object. |
65 */ |
66 */ |
66 public XClipboard(String name, String selectionName) { |
67 public XClipboard(String name, String selectionName) { |
67 super(name); |
68 super(name); |
68 selection = new XSelection(XAtom.get(selectionName), this); |
69 selection = new XSelection(XAtom.get(selectionName)); |
69 } |
70 selection.registerOwershipListener(this); |
70 |
71 } |
71 /** |
72 |
72 * The action to be run when we lose ownership |
73 /* |
73 * NOTE: This method may be called by privileged threads. |
74 * NOTE: This method may be called by privileged threads. |
74 * DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
75 * DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
75 */ |
76 */ |
76 public void run() { |
77 public void ownershipChanged(final boolean isOwner) { |
77 lostOwnershipImpl(); |
78 if (isOwner) { |
|
79 checkChangeHere(contents); |
|
80 } else { |
|
81 lostOwnershipImpl(); |
|
82 } |
78 } |
83 } |
79 |
84 |
80 protected synchronized void setContentsNative(Transferable contents) { |
85 protected synchronized void setContentsNative(Transferable contents) { |
81 SortedMap formatMap = DataTransferer.getInstance().getFormatsForTransferable |
86 SortedMap formatMap = DataTransferer.getInstance().getFormatsForTransferable |
82 (contents, DataTransferer.adaptFlavorMap(flavorMap)); |
87 (contents, DataTransferer.adaptFlavorMap(flavorMap)); |
83 long[] formats = |
88 long[] formats = DataTransferer.keysToLongArray(formatMap); |
84 DataTransferer.getInstance().keysToLongArray(formatMap); |
|
85 |
89 |
86 if (!selection.setOwner(contents, formatMap, formats, |
90 if (!selection.setOwner(contents, formatMap, formats, |
87 XToolkit.getCurrentServerTime())) { |
91 XToolkit.getCurrentServerTime())) { |
88 this.owner = null; |
92 this.owner = null; |
89 this.contents = null; |
93 this.contents = null; |
113 |
118 |
114 protected byte[] getClipboardData(long format) throws IOException { |
119 protected byte[] getClipboardData(long format) throws IOException { |
115 return selection.getData(format, XToolkit.getCurrentServerTime()); |
120 return selection.getData(format, XToolkit.getCurrentServerTime()); |
116 } |
121 } |
117 |
122 |
118 // Called on the toolkit thread under awtLock. |
123 private void checkChangeHere(Transferable contents) { |
119 public void checkChange(long[] formats) { |
|
120 if (!selection.isOwner()) { |
|
121 super.checkChange(formats); |
|
122 } |
|
123 } |
|
124 |
|
125 void checkChangeHere(Transferable contents) { |
|
126 if (areFlavorListenersRegistered()) { |
124 if (areFlavorListenersRegistered()) { |
127 super.checkChange(DataTransferer.getInstance(). |
125 checkChange(DataTransferer.getInstance(). |
128 getFormatsForTransferableAsArray(contents, flavorMap)); |
126 getFormatsForTransferableAsArray(contents, flavorMap)); |
129 } |
127 } |
130 } |
128 } |
131 |
129 |
|
130 private static int getPollInterval() { |
|
131 synchronized (XClipboard.classLock) { |
|
132 if (pollInterval <= 0) { |
|
133 pollInterval = AccessController.doPrivileged( |
|
134 new GetIntegerAction("awt.datatransfer.clipboard.poll.interval", |
|
135 defaultPollInterval)); |
|
136 if (pollInterval <= 0) { |
|
137 pollInterval = defaultPollInterval; |
|
138 } |
|
139 } |
|
140 return pollInterval; |
|
141 } |
|
142 } |
|
143 |
|
144 private XAtom getTargetsPropertyAtom() { |
|
145 if (null == targetsPropertyAtom) { |
|
146 targetsPropertyAtom = |
|
147 XAtom.get("XAWT_TARGETS_OF_SELECTION:" + selection.getSelectionAtom().getName()); |
|
148 } |
|
149 return targetsPropertyAtom; |
|
150 } |
|
151 |
132 protected void registerClipboardViewerChecked() { |
152 protected void registerClipboardViewerChecked() { |
133 if (pollInterval <= 0) { |
153 // for XConvertSelection() to be called for the first time in getTargetsDelayed() |
134 pollInterval = ((Integer)AccessController.doPrivileged( |
154 isSelectionNotifyProcessed = true; |
135 new GetIntegerAction("awt.datatransfer.clipboard.poll.interval", |
155 |
136 defaultPollInterval))).intValue(); |
|
137 if (pollInterval <= 0) { |
|
138 pollInterval = defaultPollInterval; |
|
139 } |
|
140 } |
|
141 selection.initializeSelectionForTrackingChanges(); |
|
142 boolean mustSchedule = false; |
156 boolean mustSchedule = false; |
143 synchronized (XClipboard.classLock) { |
157 synchronized (XClipboard.classLock) { |
144 if (listenedClipboards == null) { |
158 if (targetsAtom2Clipboard == null) { |
145 listenedClipboards = new HashSet(2); |
159 targetsAtom2Clipboard = new HashMap<Long, XClipboard>(2); |
146 } |
160 } |
147 mustSchedule = listenedClipboards.isEmpty(); |
161 mustSchedule = targetsAtom2Clipboard.isEmpty(); |
148 listenedClipboards.add(this); |
162 targetsAtom2Clipboard.put(getTargetsPropertyAtom().getAtom(), this); |
|
163 if (mustSchedule) { |
|
164 XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), |
|
165 new SelectionNotifyHandler()); |
|
166 } |
149 } |
167 } |
150 if (mustSchedule) { |
168 if (mustSchedule) { |
151 XToolkit.schedule(new CheckChangeTimerTask(), pollInterval); |
169 XToolkit.schedule(new CheckChangeTimerTask(), XClipboard.getPollInterval()); |
152 } |
170 } |
153 } |
171 } |
154 |
172 |
155 private static class CheckChangeTimerTask implements Runnable { |
173 private static class CheckChangeTimerTask implements Runnable { |
156 public void run() { |
174 public void run() { |
157 for (Iterator iter = listenedClipboards.iterator(); iter.hasNext();) { |
175 for (XClipboard clpbrd : targetsAtom2Clipboard.values()) { |
158 XClipboard clpbrd = (XClipboard)iter.next(); |
176 clpbrd.getTargetsDelayed(); |
159 clpbrd.selection.getTargetsDelayed(); |
|
160 } |
177 } |
161 synchronized (XClipboard.classLock) { |
178 synchronized (XClipboard.classLock) { |
162 if (listenedClipboards != null && !listenedClipboards.isEmpty()) { |
179 if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) { |
163 XToolkit.schedule(this, pollInterval); |
180 XToolkit.schedule(this, XClipboard.getPollInterval()); |
|
181 } |
|
182 } |
|
183 } |
|
184 } |
|
185 |
|
186 private static class SelectionNotifyHandler implements XEventDispatcher { |
|
187 public void dispatchEvent(XEvent ev) { |
|
188 if (ev.get_type() == XlibWrapper.SelectionNotify) { |
|
189 final XSelectionEvent xse = ev.get_xselection(); |
|
190 XClipboard clipboard = null; |
|
191 synchronized (XClipboard.classLock) { |
|
192 if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) { |
|
193 XToolkit.removeEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), this); |
|
194 return; |
|
195 } |
|
196 final long propertyAtom = xse.get_property(); |
|
197 clipboard = targetsAtom2Clipboard.get(propertyAtom); |
|
198 } |
|
199 if (null != clipboard) { |
|
200 clipboard.checkChange(xse); |
164 } |
201 } |
165 } |
202 } |
166 } |
203 } |
167 } |
204 } |
168 |
205 |
169 protected void unregisterClipboardViewerChecked() { |
206 protected void unregisterClipboardViewerChecked() { |
170 selection.deinitializeSelectionForTrackingChanges(); |
207 isSelectionNotifyProcessed = false; |
171 synchronized (XClipboard.classLock) { |
208 synchronized (XClipboard.classLock) { |
172 listenedClipboards.remove(this); |
209 targetsAtom2Clipboard.remove(getTargetsPropertyAtom().getAtom()); |
173 } |
210 } |
174 } |
211 } |
175 |
212 |
|
213 // checkChange() will be called on SelectionNotify |
|
214 private void getTargetsDelayed() { |
|
215 XToolkit.awtLock(); |
|
216 try { |
|
217 long curTime = System.currentTimeMillis(); |
|
218 if (isSelectionNotifyProcessed || curTime >= (convertSelectionTime + UNIXToolkit.getDatatransferTimeout())) |
|
219 { |
|
220 convertSelectionTime = curTime; |
|
221 XlibWrapper.XConvertSelection(XToolkit.getDisplay(), |
|
222 selection.getSelectionAtom().getAtom(), |
|
223 XDataTransferer.TARGETS_ATOM.getAtom(), |
|
224 getTargetsPropertyAtom().getAtom(), |
|
225 XWindow.getXAWTRootWindow().getWindow(), |
|
226 XlibWrapper.CurrentTime); |
|
227 isSelectionNotifyProcessed = false; |
|
228 } |
|
229 } finally { |
|
230 XToolkit.awtUnlock(); |
|
231 } |
|
232 } |
|
233 |
|
234 /* |
|
235 * Tracks changes of available formats. |
|
236 * NOTE: This method may be called by privileged threads. |
|
237 * DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
|
238 */ |
|
239 private void checkChange(XSelectionEvent xse) { |
|
240 final long propertyAtom = xse.get_property(); |
|
241 if (propertyAtom != getTargetsPropertyAtom().getAtom()) { |
|
242 // wrong atom |
|
243 return; |
|
244 } |
|
245 |
|
246 final XAtom selectionAtom = XAtom.get(xse.get_selection()); |
|
247 final XSelection changedSelection = XSelection.getSelection(selectionAtom); |
|
248 |
|
249 if (null == changedSelection || changedSelection != selection) { |
|
250 // unknown selection - do nothing |
|
251 return; |
|
252 } |
|
253 |
|
254 isSelectionNotifyProcessed = true; |
|
255 |
|
256 if (selection.isOwner()) { |
|
257 // selection is owner - do not need formats |
|
258 return; |
|
259 } |
|
260 |
|
261 long[] formats = null; |
|
262 |
|
263 if (propertyAtom == XlibWrapper.None) { |
|
264 // We treat None property atom as "empty selection". |
|
265 formats = new long[0]; |
|
266 } else { |
|
267 WindowPropertyGetter targetsGetter = |
|
268 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(), |
|
269 XAtom.get(propertyAtom), 0, |
|
270 XSelection.MAX_LENGTH, true, |
|
271 XlibWrapper.AnyPropertyType); |
|
272 try { |
|
273 targetsGetter.execute(); |
|
274 formats = XSelection.getFormats(targetsGetter); |
|
275 } finally { |
|
276 targetsGetter.dispose(); |
|
277 } |
|
278 } |
|
279 |
|
280 checkChange(formats); |
|
281 } |
176 } |
282 } |