KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > NbClipboard


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.core;
21
22 import java.awt.AWTEvent JavaDoc;
23 import java.awt.Toolkit JavaDoc;
24 import java.awt.datatransfer.Clipboard JavaDoc;
25 import java.awt.datatransfer.ClipboardOwner JavaDoc;
26 import java.awt.datatransfer.DataFlavor JavaDoc;
27 import java.awt.datatransfer.FlavorEvent JavaDoc;
28 import java.awt.datatransfer.FlavorListener JavaDoc;
29 import java.awt.datatransfer.Transferable JavaDoc;
30 import java.awt.datatransfer.UnsupportedFlavorException JavaDoc;
31 import java.awt.event.AWTEventListener JavaDoc;
32 import java.awt.event.WindowEvent JavaDoc;
33 import java.lang.ref.Reference JavaDoc;
34 import java.lang.ref.WeakReference JavaDoc;
35 import java.util.Collection JavaDoc;
36 import java.util.logging.Level JavaDoc;
37 import java.util.logging.Logger JavaDoc;
38 import org.openide.util.Exceptions;
39 import org.openide.util.Lookup;
40 import org.openide.util.LookupEvent;
41 import org.openide.util.LookupListener;
42 import org.openide.util.RequestProcessor;
43 import org.openide.util.datatransfer.ExClipboard;
44
45 public final class NbClipboard extends ExClipboard
46 implements LookupListener, Runnable JavaDoc, FlavorListener JavaDoc, AWTEventListener JavaDoc
47 {
48     private Logger JavaDoc log;
49     private Clipboard JavaDoc systemClipboard;
50     private ExClipboard.Convertor[] convertors;
51     private Lookup.Result<ExClipboard.Convertor> result;
52     final boolean slowSystemClipboard;
53     private Transferable JavaDoc last;
54     private long lastWindowActivated;
55     private long lastWindowDeactivated;
56     private Reference JavaDoc<Object JavaDoc> lastWindowDeactivatedSource = new WeakReference JavaDoc<Object JavaDoc>(null);
57
58     public NbClipboard() {
59         super("NBClipboard"); // NOI18N
60
systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
61         log = Logger.getLogger("org.netbeans.core.NbClipboard"); // NOI18N
62

63         result = Lookup.getDefault().lookupResult(ExClipboard.Convertor.class);
64         result.addLookupListener(this);
65
66         systemClipboard.addFlavorListener(this);
67
68         resultChanged(null);
69
70         if (System.getProperty("netbeans.slow.system.clipboard.hack") != null) // NOI18N
71
slowSystemClipboard = Boolean.getBoolean("netbeans.slow.system.clipboard.hack"); // NOI18N
72
else
73             slowSystemClipboard = true;
74
75
76
77
78         if (!slowSystemClipboard) {
79             if (System.getProperty("sun.awt.datatransfer.timeout") == null) { // NOI18N
80
System.setProperty("sun.awt.datatransfer.timeout", "1000"); // NOI18N
81
}
82         } else {
83             Toolkit.getDefaultToolkit().addAWTEventListener(
84                 this, AWTEvent.WINDOW_EVENT_MASK);
85         }
86     }
87     
88     protected synchronized ExClipboard.Convertor[] getConvertors () {
89         return convertors;
90     }
91
92     public synchronized void resultChanged(LookupEvent ev) {
93         Collection JavaDoc<? extends ExClipboard.Convertor> c = result.allInstances();
94         ExClipboard.Convertor[] temp = new ExClipboard.Convertor[c.size()];
95         convertors = c.toArray(temp);
96     }
97
98     // XXX(-ttran) on Unix (and also on Windows as we discovered recently)
99
// calling getContents() on the system clipboard is very expensive, the
100
// call can take up to 1000 milliseconds. We need to examine the clipboard
101
// contents each time the Node is activated, the method must be fast.
102
// Therefore we cache the contents of system clipboard and use the cache
103
// when someone calls getContents(). The cache is sync'ed with the system
104
// clipboard when _any_ of our Windows gets WINDOW_ACTIVATED event. It
105
// means if some other apps modify the contents of the system clipboard in
106
// the background then the change won't be propagated to us immediately.
107
// The other drawback is that if module code bypasses NBClipboard and
108
// accesses the system clipboard directly then we don't see these changes.
109
//
110
// The other problem is an AWT bug
111
//
112
// http://developer.java.sun.com/developer/bugParade/bugs/4818143.html
113
//
114
// sun.awt.datatransfer.ClipboardTransferable.getClipboardData() can hang
115
// for very long time (maxlong == eternity). We tries to avoid the hang by
116
// access the system clipboard from a separate thread. If the hang happens
117
// the thread will wait for the system clipboard forever but not the whole
118
// IDE
119

120     private RequestProcessor.Task syncTask =
121         new RequestProcessor("System clipboard synchronizer").create(this, true); // NOI18N
122

123     private Transferable JavaDoc data;
124     private ClipboardOwner JavaDoc dataOwner;
125     
126     public void setContents(Transferable JavaDoc contents, ClipboardOwner JavaDoc owner) {
127         synchronized (this) {
128             // XXX(-dstrupl) the following line might lead to a double converted
129
// transferable. Can be fixed as Jesse describes in #32485
130
if (log.isLoggable (Level.FINER)) {
131                 log.log (Level.FINER, "setContents called with: "); // NOI18N
132
logFlavors (contents, Level.FINER);
133             }
134             contents = convert(contents);
135             if (log.isLoggable (Level.FINER)) {
136                 log.log (Level.FINER, "After conversion:"); // NOI18N
137
logFlavors (contents, Level.FINER);
138             }
139
140             if (slowSystemClipboard) {
141                 super.setContents(contents, owner);
142             } else {
143                 if (last != null) transferableOwnershipLost(last);
144                 last = contents;
145             }
146
147             data = contents;
148             dataOwner = owner;
149             syncTask.schedule(0);
150         }
151         fireClipboardChange();
152     }
153
154     public Transferable JavaDoc getContents(Object JavaDoc requestor) {
155         Transferable JavaDoc prev;
156
157         try {
158             if (slowSystemClipboard) {
159                 // The purpose of lastWindowActivated+100 is to ignore calls
160
// which immediatelly follow WINDOW_ACTIVATED event.
161
// This is workaround of JDK bug described in issue 41098.
162
if (lastWindowActivated != 0 && lastWindowActivated+100 < System.currentTimeMillis()) {
163                     lastWindowActivated = 0;
164                     
165                     syncTask.schedule(0);
166                     syncTask.waitFinished (100);
167                 }
168                 
169                 
170                 prev = super.getContents (requestor);
171             } else {
172                 syncTask.waitFinished ();
173                 prev = systemClipboard.getContents (requestor);
174             }
175
176             synchronized (this) {
177                 if (log.isLoggable (Level.FINE)) {
178                     log.log (Level.FINE, "getContents by " + requestor); // NOI18N
179
logFlavors (prev, Level.FINE);
180                 }
181                 if (prev == null) // if system clipboard has no contents
182
return null;
183
184                 Transferable JavaDoc res = convert (prev);
185                 if (log.isLoggable (Level.FINE)) {
186                     log.log (Level.FINE, "getContents by " + requestor); // NOI18N
187
logFlavors (res, Level.FINE);
188
189                     res = new LoggableTransferable (res);
190                 }
191                 return res;
192             }
193         } catch (ThreadDeath JavaDoc ex) {
194             throw ex;
195         } catch (InterruptedException JavaDoc ex) {
196             Logger.getLogger(NbClipboard.class.getName()).log(Level.WARNING, null, ex);
197             return null;
198         } catch (Throwable JavaDoc ex) {
199             Exceptions.printStackTrace(ex);
200             return null;
201         }
202     }
203
204     public void run() {
205         Transferable JavaDoc contents = null;
206         ClipboardOwner JavaDoc owner = null;
207
208         synchronized (this) {
209             if (data != null) {
210              contents = data;
211              owner = dataOwner;
212             }
213             data = null;
214             dataOwner = null;
215         }
216         if (contents != null) {
217             if (log.isLoggable (Level.FINE)) {
218                 log.log (Level.FINE, "systemClipboard updated:"); // NOI18N
219
logFlavors (contents, Level.FINE);
220             }
221             systemClipboard.setContents(contents, owner);
222             return;
223         }
224
225         try {
226             Transferable JavaDoc transferable = systemClipboard.getContents(this);
227             super.setContents(transferable, null);
228             if (log.isLoggable (Level.FINE)) {
229                 log.log (Level.FINE, "internal clipboard updated:"); // NOI18N
230
logFlavors (transferable, Level.FINE);
231             }
232             fireClipboardChange();
233         }
234         catch (ThreadDeath JavaDoc ex) {
235             throw ex;
236         }
237         catch (Throwable JavaDoc ignore) {
238         }
239     }
240
241     /** For testing purposes.
242      */

243     final void waitFinished () {
244         syncTask.waitFinished ();
245     }
246     
247     final void activateWindowHack (boolean reschedule) {
248         // if WINDOW_DEACTIVATED is followed immediatelly with
249
// WINDOW_ACTIVATED then it is JDK bug described in
250
// issue 41098.
251
lastWindowActivated = System.currentTimeMillis();
252         if (reschedule) {
253             syncTask.schedule (0);
254         }
255     }
256     
257     private void logFlavors (Transferable JavaDoc trans, Level JavaDoc level) {
258         if (trans == null)
259             log.log (level, " no clipboard contents");
260         else {
261             java.awt.datatransfer.DataFlavor JavaDoc[] arr = trans.getTransferDataFlavors();
262             for (int i = 0; i < arr.length; i++) {
263                 log.log (level, " " + i + " = " + arr[i]);
264             }
265         }
266     }
267
268     public void flavorsChanged(FlavorEvent JavaDoc e) {
269         fireClipboardChange();
270     }
271
272     public void eventDispatched(AWTEvent JavaDoc ev) {
273         if (!(ev instanceof WindowEvent JavaDoc))
274             return;
275
276         if (ev.getID() == WindowEvent.WINDOW_DEACTIVATED) {
277             lastWindowDeactivated = System.currentTimeMillis();
278             lastWindowDeactivatedSource = new WeakReference JavaDoc<Object JavaDoc>(ev.getSource());
279         }
280         if (ev.getID() == WindowEvent.WINDOW_ACTIVATED) {
281             if (System.currentTimeMillis() - lastWindowDeactivated < 100 &&
282                 ev.getSource() == lastWindowDeactivatedSource.get()) {
283                 activateWindowHack (false);
284             }
285             if (log.isLoggable (Level.FINE)) {
286                 log.log (Level.FINE, "window activated scheduling update"); // NOI18N
287
}
288             syncTask.schedule(0);
289         }
290     }
291     
292     /** Transferable that logs operations on itself.
293      */

294     private final class LoggableTransferable implements Transferable JavaDoc {
295         private Transferable JavaDoc delegate;
296         
297         public LoggableTransferable (Transferable JavaDoc delegate) {
298             this.delegate = delegate;
299         }
300         public Object JavaDoc getTransferData (DataFlavor JavaDoc flavor) throws UnsupportedFlavorException JavaDoc, java.io.IOException JavaDoc {
301             log.log (Level.FINE, "Request for flavor: " + flavor); // NOI18N
302
Object JavaDoc res = delegate.getTransferData (flavor);
303             log.log (Level.FINE, "Returning value: " + res); // NOI18N
304
return res;
305         }
306         
307         public DataFlavor JavaDoc[] getTransferDataFlavors () {
308             return delegate.getTransferDataFlavors ();
309         }
310         
311         public boolean isDataFlavorSupported (DataFlavor JavaDoc flavor) {
312             boolean res = delegate.isDataFlavorSupported (flavor);
313             log.log (Level.FINE, "isDataFlavorSupported: " + flavor + " result: " + res); // NOI18N
314
return res;
315         }
316         
317     }
318 }
319
Popular Tags