23 * questions.
24 */
25
26 package sun.awt.datatransfer;
27
28 import java.awt.EventQueue;
29
30 import java.awt.datatransfer.Clipboard;
31 import java.awt.datatransfer.FlavorTable;
32 import java.awt.datatransfer.SystemFlavorMap;
33 import java.awt.datatransfer.Transferable;
34 import java.awt.datatransfer.ClipboardOwner;
35 import java.awt.datatransfer.DataFlavor;
36 import java.awt.datatransfer.FlavorListener;
37 import java.awt.datatransfer.FlavorEvent;
38 import java.awt.datatransfer.UnsupportedFlavorException;
39
40 import java.beans.PropertyChangeEvent;
41 import java.beans.PropertyChangeListener;
42
43 import java.util.Iterator;
44 import java.util.Set;
45 import java.util.HashSet;
46
47 import java.io.IOException;
48
49 import sun.awt.AppContext;
50 import sun.awt.PeerEvent;
51 import sun.awt.SunToolkit;
52 import sun.awt.EventListenerAggregate;
53
54
55 /**
56 * Serves as a common, helper superclass for the Win32 and X11 system
57 * Clipboards.
58 *
59 * @author Danila Sinopalnikov
60 * @author Alexander Gerasimov
61 *
62 * @since 1.3
63 */
64 public abstract class SunClipboard extends Clipboard
65 implements PropertyChangeListener {
66
67 public static final FlavorTable flavorMap =
68 (FlavorTable)SystemFlavorMap.getDefaultFlavorMap();
69
70 private AppContext contentsContext = null;
71
72 private final Object CLIPBOARD_FLAVOR_LISTENER_KEY;
93 public synchronized void setContents(Transferable contents,
94 ClipboardOwner owner) {
95 // 4378007 : Toolkit.getSystemClipboard().setContents(null, null)
96 // should throw NPE
97 if (contents == null) {
98 throw new NullPointerException("contents");
99 }
100
101 initContext();
102
103 final ClipboardOwner oldOwner = this.owner;
104 final Transferable oldContents = this.contents;
105
106 try {
107 this.owner = owner;
108 this.contents = new TransferableProxy(contents, true);
109
110 setContentsNative(contents);
111 } finally {
112 if (oldOwner != null && oldOwner != owner) {
113 EventQueue.invokeLater(new Runnable() {
114 public void run() {
115 oldOwner.lostOwnership(SunClipboard.this, oldContents);
116 }
117 });
118 }
119 }
120 }
121
122 private synchronized void initContext() {
123 final AppContext context = AppContext.getAppContext();
124
125 if (contentsContext != context) {
126 // Need to synchronize on the AppContext to guarantee that it cannot
127 // be disposed after the check, but before the listener is added.
128 synchronized (context) {
129 if (context.isDisposed()) {
130 throw new IllegalStateException("Can't set contents from disposed AppContext");
131 }
132 context.addPropertyChangeListener
133 (AppContext.DISPOSED_PROPERTY_NAME, this);
134 }
135 if (contentsContext != null) {
136 contentsContext.removePropertyChangeListener
137 (AppContext.DISPOSED_PROPERTY_NAME, this);
341 *
342 * @throws IllegalStateException if formats could not be retrieved
343 */
344 protected abstract long[] getClipboardFormats();
345
346 protected abstract byte[] getClipboardData(long format) throws IOException;
347
348
349 private static Set formatArrayAsDataFlavorSet(long[] formats) {
350 return (formats == null) ? null :
351 DataTransferer.getInstance().
352 getFlavorsForFormatsAsSet(formats, flavorMap);
353 }
354
355
356 public synchronized void addFlavorListener(FlavorListener listener) {
357 if (listener == null) {
358 return;
359 }
360 AppContext appContext = AppContext.getAppContext();
361 EventListenerAggregate contextFlavorListeners = (EventListenerAggregate)
362 appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
363 if (contextFlavorListeners == null) {
364 contextFlavorListeners = new EventListenerAggregate(FlavorListener.class);
365 appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, contextFlavorListeners);
366 }
367 contextFlavorListeners.add(listener);
368
369 if (numberOfFlavorListeners++ == 0) {
370 long[] currentFormats = null;
371 try {
372 openClipboard(null);
373 currentFormats = getClipboardFormats();
374 } catch (IllegalStateException exc) {
375 } finally {
376 closeClipboard();
377 }
378 currentDataFlavors = formatArrayAsDataFlavorSet(currentFormats);
379
380 registerClipboardViewerChecked();
381 }
382 }
383
384 public synchronized void removeFlavorListener(FlavorListener listener) {
385 if (listener == null) {
386 return;
387 }
388 AppContext appContext = AppContext.getAppContext();
389 EventListenerAggregate contextFlavorListeners = (EventListenerAggregate)
390 appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
391 if (contextFlavorListeners == null){
392 //else we throw NullPointerException, but it is forbidden
393 return;
394 }
395 if (contextFlavorListeners.remove(listener) &&
396 --numberOfFlavorListeners == 0) {
397 unregisterClipboardViewerChecked();
398 currentDataFlavors = null;
399 }
400 }
401
402 public synchronized FlavorListener[] getFlavorListeners() {
403 EventListenerAggregate contextFlavorListeners = (EventListenerAggregate)
404 AppContext.getAppContext().get(CLIPBOARD_FLAVOR_LISTENER_KEY);
405 return contextFlavorListeners == null ? new FlavorListener[0] :
406 (FlavorListener[])contextFlavorListeners.getListenersCopy();
407 }
408
409 public boolean areFlavorListenersRegistered() {
410 return (numberOfFlavorListeners > 0);
411 }
412
413 protected abstract void registerClipboardViewerChecked();
414
415 protected abstract void unregisterClipboardViewerChecked();
416
417 /**
418 * Checks change of the <code>DataFlavor</code>s and, if necessary,
419 * posts notifications on <code>FlavorEvent</code>s to the
420 * AppContexts' EDTs.
421 * The parameter <code>formats</code> is null iff we have just
422 * failed to get formats available on the clipboard.
423 *
424 * @param formats data formats that have just been retrieved from
425 * this clipboard
426 */
427 public void checkChange(long[] formats) {
428 Set prevDataFlavors = currentDataFlavors;
429 currentDataFlavors = formatArrayAsDataFlavorSet(formats);
430
431 if ((prevDataFlavors != null) && (currentDataFlavors != null) &&
432 prevDataFlavors.equals(currentDataFlavors)) {
433 // we've been able to successfully get available on the clipboard
434 // DataFlavors this and previous time and they are coincident;
435 // don't notify
436 return;
437 }
438
439 class SunFlavorChangeNotifier implements Runnable {
440 private final FlavorListener flavorListener;
441
442 SunFlavorChangeNotifier(FlavorListener flavorListener) {
443 this.flavorListener = flavorListener;
444 }
445
446 public void run() {
447 if (flavorListener != null) {
448 flavorListener.flavorsChanged(new FlavorEvent(SunClipboard.this));
449 }
450 }
451 };
452
453 for (Iterator it = AppContext.getAppContexts().iterator(); it.hasNext();) {
454 AppContext appContext = (AppContext)it.next();
455 if (appContext == null || appContext.isDisposed()) {
456 continue;
457 }
458 EventListenerAggregate flavorListeners = (EventListenerAggregate)
459 appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
460 if (flavorListeners != null) {
461 FlavorListener[] flavorListenerArray =
462 (FlavorListener[])flavorListeners.getListenersInternal();
463 for (int i = 0; i < flavorListenerArray.length; i++) {
464 SunToolkit.postEvent(appContext, new PeerEvent(this,
465 new SunFlavorChangeNotifier(flavorListenerArray[i]),
466 PeerEvent.PRIORITY_EVENT));
467 }
468 }
469 }
470 }
471
472 }
|
23 * questions.
24 */
25
26 package sun.awt.datatransfer;
27
28 import java.awt.EventQueue;
29
30 import java.awt.datatransfer.Clipboard;
31 import java.awt.datatransfer.FlavorTable;
32 import java.awt.datatransfer.SystemFlavorMap;
33 import java.awt.datatransfer.Transferable;
34 import java.awt.datatransfer.ClipboardOwner;
35 import java.awt.datatransfer.DataFlavor;
36 import java.awt.datatransfer.FlavorListener;
37 import java.awt.datatransfer.FlavorEvent;
38 import java.awt.datatransfer.UnsupportedFlavorException;
39
40 import java.beans.PropertyChangeEvent;
41 import java.beans.PropertyChangeListener;
42
43 import java.util.Objects;
44 import java.util.Set;
45 import java.util.HashSet;
46
47 import java.io.IOException;
48
49 import sun.awt.AppContext;
50 import sun.awt.PeerEvent;
51 import sun.awt.SunToolkit;
52
53
54 /**
55 * Serves as a common, helper superclass for the Win32 and X11 system
56 * Clipboards.
57 *
58 * @author Danila Sinopalnikov
59 * @author Alexander Gerasimov
60 *
61 * @since 1.3
62 */
63 public abstract class SunClipboard extends Clipboard
64 implements PropertyChangeListener {
65
66 public static final FlavorTable flavorMap =
67 (FlavorTable)SystemFlavorMap.getDefaultFlavorMap();
68
69 private AppContext contentsContext = null;
70
71 private final Object CLIPBOARD_FLAVOR_LISTENER_KEY;
92 public synchronized void setContents(Transferable contents,
93 ClipboardOwner owner) {
94 // 4378007 : Toolkit.getSystemClipboard().setContents(null, null)
95 // should throw NPE
96 if (contents == null) {
97 throw new NullPointerException("contents");
98 }
99
100 initContext();
101
102 final ClipboardOwner oldOwner = this.owner;
103 final Transferable oldContents = this.contents;
104
105 try {
106 this.owner = owner;
107 this.contents = new TransferableProxy(contents, true);
108
109 setContentsNative(contents);
110 } finally {
111 if (oldOwner != null && oldOwner != owner) {
112 EventQueue.invokeLater(() -> oldOwner.lostOwnership(SunClipboard.this, oldContents));
113 }
114 }
115 }
116
117 private synchronized void initContext() {
118 final AppContext context = AppContext.getAppContext();
119
120 if (contentsContext != context) {
121 // Need to synchronize on the AppContext to guarantee that it cannot
122 // be disposed after the check, but before the listener is added.
123 synchronized (context) {
124 if (context.isDisposed()) {
125 throw new IllegalStateException("Can't set contents from disposed AppContext");
126 }
127 context.addPropertyChangeListener
128 (AppContext.DISPOSED_PROPERTY_NAME, this);
129 }
130 if (contentsContext != null) {
131 contentsContext.removePropertyChangeListener
132 (AppContext.DISPOSED_PROPERTY_NAME, this);
336 *
337 * @throws IllegalStateException if formats could not be retrieved
338 */
339 protected abstract long[] getClipboardFormats();
340
341 protected abstract byte[] getClipboardData(long format) throws IOException;
342
343
344 private static Set formatArrayAsDataFlavorSet(long[] formats) {
345 return (formats == null) ? null :
346 DataTransferer.getInstance().
347 getFlavorsForFormatsAsSet(formats, flavorMap);
348 }
349
350
351 public synchronized void addFlavorListener(FlavorListener listener) {
352 if (listener == null) {
353 return;
354 }
355 AppContext appContext = AppContext.getAppContext();
356 Set<FlavorListener> flavorListeners = getFlavorListeners(appContext);
357 if (flavorListeners == null) {
358 flavorListeners = new HashSet<>();
359 appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, flavorListeners);
360 }
361 flavorListeners.add(listener);
362
363 if (numberOfFlavorListeners++ == 0) {
364 long[] currentFormats = null;
365 try {
366 openClipboard(null);
367 currentFormats = getClipboardFormats();
368 } catch (IllegalStateException exc) {
369 } finally {
370 closeClipboard();
371 }
372 currentDataFlavors = formatArrayAsDataFlavorSet(currentFormats);
373
374 registerClipboardViewerChecked();
375 }
376 }
377
378 public synchronized void removeFlavorListener(FlavorListener listener) {
379 if (listener == null) {
380 return;
381 }
382 Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext());
383 if (flavorListeners == null){
384 //else we throw NullPointerException, but it is forbidden
385 return;
386 }
387 if (flavorListeners.remove(listener) && --numberOfFlavorListeners == 0) {
388 unregisterClipboardViewerChecked();
389 currentDataFlavors = null;
390 }
391 }
392
393 @SuppressWarnings("unchecked")
394 private Set<FlavorListener> getFlavorListeners(AppContext appContext) {
395 return (Set<FlavorListener>)appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
396 }
397
398 public synchronized FlavorListener[] getFlavorListeners() {
399 Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext());
400 return flavorListeners == null ? new FlavorListener[0]
401 : flavorListeners.toArray(new FlavorListener[flavorListeners.size()]);
402 }
403
404 public boolean areFlavorListenersRegistered() {
405 return (numberOfFlavorListeners > 0);
406 }
407
408 protected abstract void registerClipboardViewerChecked();
409
410 protected abstract void unregisterClipboardViewerChecked();
411
412 /**
413 * Checks change of the <code>DataFlavor</code>s and, if necessary,
414 * posts notifications on <code>FlavorEvent</code>s to the
415 * AppContexts' EDTs.
416 * The parameter <code>formats</code> is null iff we have just
417 * failed to get formats available on the clipboard.
418 *
419 * @param formats data formats that have just been retrieved from
420 * this clipboard
421 */
422 public synchronized void checkChange(long[] formats) {
423 Set prevDataFlavors = currentDataFlavors;
424 currentDataFlavors = formatArrayAsDataFlavorSet(formats);
425
426 if ((prevDataFlavors != null) && (currentDataFlavors != null) &&
427 prevDataFlavors.equals(currentDataFlavors)) {
428 // we've been able to successfully get available on the clipboard
429 // DataFlavors this and previous time and they are coincident;
430 // don't notify
431 return;
432 }
433
434 for (AppContext appContext : AppContext.getAppContexts()) {
435 if (appContext == null || appContext.isDisposed()) {
436 continue;
437 }
438 Set<FlavorListener> flavorListeners = getFlavorListeners(appContext);
439 if (flavorListeners != null) {
440 flavorListeners.stream()
441 .filter(Objects::nonNull)
442 .forEach(listener -> SunToolkit.postEvent(appContext,
443 new PeerEvent(this,
444 () -> listener.flavorsChanged(new FlavorEvent(SunClipboard.this)),
445 PeerEvent.PRIORITY_EVENT)));
446 }
447 }
448 }
449
450 }
|