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 private AppContext contentsContext = null; 67 68 private final Object CLIPBOARD_FLAVOR_LISTENER_KEY; 69 70 /** 71 * A number of <code>FlavorListener</code>s currently registered 72 * on this clipboard across all <code>AppContext</code>s. 73 */ 74 private volatile int numberOfFlavorListeners = 0; 75 76 /** 77 * A set of <code>DataFlavor</code>s that is available on 78 * this clipboard. It is used for tracking changes 79 * of <code>DataFlavor</code>s available on this clipboard. 80 */ 81 private volatile Set<DataFlavor> currentDataFlavors; 82 83 84 public SunClipboard(String name) { 85 super(name); 86 CLIPBOARD_FLAVOR_LISTENER_KEY = new StringBuffer(name + "_CLIPBOARD_FLAVOR_LISTENER_KEY"); 87 } 88 89 public synchronized void setContents(Transferable contents, 90 ClipboardOwner owner) { 91 // 4378007 : Toolkit.getSystemClipboard().setContents(null, null) 92 // should throw NPE 93 if (contents == null) { 94 throw new NullPointerException("contents"); 95 } 96 97 initContext(); 98 99 final ClipboardOwner oldOwner = this.owner; 100 final Transferable oldContents = this.contents; 101 341 private static Set<DataFlavor> formatArrayAsDataFlavorSet(long[] formats) { 342 return (formats == null) ? null : 343 DataTransferer.getInstance(). 344 getFlavorsForFormatsAsSet(formats, getDefaultFlavorTable()); 345 } 346 347 348 public synchronized void addFlavorListener(FlavorListener listener) { 349 if (listener == null) { 350 return; 351 } 352 AppContext appContext = AppContext.getAppContext(); 353 Set<FlavorListener> flavorListeners = getFlavorListeners(appContext); 354 if (flavorListeners == null) { 355 flavorListeners = new HashSet<>(); 356 appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, flavorListeners); 357 } 358 flavorListeners.add(listener); 359 360 if (numberOfFlavorListeners++ == 0) { 361 long[] currentFormats = null; 362 try { 363 openClipboard(null); 364 currentFormats = getClipboardFormats(); 365 } catch (IllegalStateException exc) { 366 } finally { 367 closeClipboard(); 368 } 369 currentDataFlavors = formatArrayAsDataFlavorSet(currentFormats); 370 371 registerClipboardViewerChecked(); 372 } 373 } 374 375 public synchronized void removeFlavorListener(FlavorListener listener) { 376 if (listener == null) { 377 return; 378 } 379 Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext()); 380 if (flavorListeners == null){ 381 //else we throw NullPointerException, but it is forbidden 382 return; 383 } 384 if (flavorListeners.remove(listener) && --numberOfFlavorListeners == 0) { 385 unregisterClipboardViewerChecked(); 386 currentDataFlavors = null; 387 } 388 } 389 390 @SuppressWarnings("unchecked") 391 private Set<FlavorListener> getFlavorListeners(AppContext appContext) { 392 return (Set<FlavorListener>)appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY); 393 } 394 395 public synchronized FlavorListener[] getFlavorListeners() { 396 Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext()); 397 return flavorListeners == null ? new FlavorListener[0] 398 : flavorListeners.toArray(new FlavorListener[flavorListeners.size()]); 399 } 400 401 public boolean areFlavorListenersRegistered() { 402 return (numberOfFlavorListeners > 0); 403 } 404 405 protected abstract void registerClipboardViewerChecked(); 406 407 protected abstract void unregisterClipboardViewerChecked(); 408 409 /** 410 * Checks change of the <code>DataFlavor</code>s and, if necessary, 411 * posts notifications on <code>FlavorEvent</code>s to the 412 * AppContexts' EDTs. 413 * The parameter <code>formats</code> is null iff we have just 414 * failed to get formats available on the clipboard. 415 * 416 * @param formats data formats that have just been retrieved from 417 * this clipboard 418 */ 419 public void checkChange(long[] formats) { 420 Set<DataFlavor> prevDataFlavors = currentDataFlavors; 421 currentDataFlavors = formatArrayAsDataFlavorSet(formats); 422 423 if (Objects.equals(prevDataFlavors, currentDataFlavors)) { 424 // we've been able to successfully get available on the clipboard 425 // DataFlavors this and previous time and they are coincident; 426 // don't notify 427 return; 428 } 429 430 for (AppContext appContext : AppContext.getAppContexts()) { 431 if (appContext == null || appContext.isDisposed()) { 432 continue; 433 } 434 Set<FlavorListener> flavorListeners = getFlavorListeners(appContext); 435 if (flavorListeners != null) { 436 for (FlavorListener listener : flavorListeners) { 437 if (listener != null) { 438 PeerEvent peerEvent = new PeerEvent(this, 439 () -> listener.flavorsChanged(new FlavorEvent(SunClipboard.this)), 440 PeerEvent.PRIORITY_EVENT); 441 SunToolkit.postEvent(appContext, peerEvent); 442 } 443 } | 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.Arrays; 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 private AppContext contentsContext = null; 67 68 private final Object CLIPBOARD_FLAVOR_LISTENER_KEY; 69 70 /** 71 * A number of <code>FlavorListener</code>s currently registered 72 * on this clipboard across all <code>AppContext</code>s. 73 */ 74 private volatile int numberOfFlavorListeners = 0; 75 76 /** 77 * A set of formats that is available on 78 * this clipboard. It is used for tracking changes 79 * of <code>DataFlavor</code>s available on this clipboard. 80 */ 81 private volatile long[] currentDataFormats; 82 83 84 public SunClipboard(String name) { 85 super(name); 86 CLIPBOARD_FLAVOR_LISTENER_KEY = new StringBuffer(name + "_CLIPBOARD_FLAVOR_LISTENER_KEY"); 87 } 88 89 public synchronized void setContents(Transferable contents, 90 ClipboardOwner owner) { 91 // 4378007 : Toolkit.getSystemClipboard().setContents(null, null) 92 // should throw NPE 93 if (contents == null) { 94 throw new NullPointerException("contents"); 95 } 96 97 initContext(); 98 99 final ClipboardOwner oldOwner = this.owner; 100 final Transferable oldContents = this.contents; 101 341 private static Set<DataFlavor> formatArrayAsDataFlavorSet(long[] formats) { 342 return (formats == null) ? null : 343 DataTransferer.getInstance(). 344 getFlavorsForFormatsAsSet(formats, getDefaultFlavorTable()); 345 } 346 347 348 public synchronized void addFlavorListener(FlavorListener listener) { 349 if (listener == null) { 350 return; 351 } 352 AppContext appContext = AppContext.getAppContext(); 353 Set<FlavorListener> flavorListeners = getFlavorListeners(appContext); 354 if (flavorListeners == null) { 355 flavorListeners = new HashSet<>(); 356 appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, flavorListeners); 357 } 358 flavorListeners.add(listener); 359 360 if (numberOfFlavorListeners++ == 0) { 361 currentDataFormats = getClipboardFormatsOpenClose(); 362 registerClipboardViewerChecked(); 363 } 364 } 365 366 public synchronized void removeFlavorListener(FlavorListener listener) { 367 if (listener == null) { 368 return; 369 } 370 Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext()); 371 if (flavorListeners == null){ 372 //else we throw NullPointerException, but it is forbidden 373 return; 374 } 375 if (flavorListeners.remove(listener) && --numberOfFlavorListeners == 0) { 376 unregisterClipboardViewerChecked(); 377 currentDataFormats = null; 378 } 379 } 380 381 @SuppressWarnings("unchecked") 382 private Set<FlavorListener> getFlavorListeners(AppContext appContext) { 383 return (Set<FlavorListener>)appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY); 384 } 385 386 public synchronized FlavorListener[] getFlavorListeners() { 387 Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext()); 388 return flavorListeners == null ? new FlavorListener[0] 389 : flavorListeners.toArray(new FlavorListener[flavorListeners.size()]); 390 } 391 392 public boolean areFlavorListenersRegistered() { 393 return (numberOfFlavorListeners > 0); 394 } 395 396 protected abstract void registerClipboardViewerChecked(); 397 398 protected abstract void unregisterClipboardViewerChecked(); 399 400 /** 401 * Checks change of the <code>DataFlavor</code>s and, if necessary, 402 * posts notifications on <code>FlavorEvent</code>s to the 403 * AppContexts' EDTs. 404 * The parameter <code>formats</code> is null iff we have just 405 * failed to get formats available on the clipboard. 406 * 407 * @param formats data formats that have just been retrieved from 408 * this clipboard 409 */ 410 public void checkChange(long[] formats) { 411 long[] prevDataFlavors = currentDataFormats; 412 currentDataFormats = formats; 413 414 if (Arrays.equals(prevDataFlavors, currentDataFormats)) { 415 // we've been able to successfully get available on the clipboard 416 // DataFlavors this and previous time and they are coincident; 417 // don't notify 418 return; 419 } 420 421 for (AppContext appContext : AppContext.getAppContexts()) { 422 if (appContext == null || appContext.isDisposed()) { 423 continue; 424 } 425 Set<FlavorListener> flavorListeners = getFlavorListeners(appContext); 426 if (flavorListeners != null) { 427 for (FlavorListener listener : flavorListeners) { 428 if (listener != null) { 429 PeerEvent peerEvent = new PeerEvent(this, 430 () -> listener.flavorsChanged(new FlavorEvent(SunClipboard.this)), 431 PeerEvent.PRIORITY_EVENT); 432 SunToolkit.postEvent(appContext, peerEvent); 433 } 434 } |