mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			676 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			676 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* GtkClipboard.java - Class representing gtk+ clipboard selection.
 | |
|    Copyright (C) 2005 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| 
 | |
| package gnu.java.awt.peer.gtk;
 | |
| 
 | |
| import gnu.classpath.Pointer;
 | |
| 
 | |
| import java.awt.datatransfer.*;
 | |
| 
 | |
| import java.io.*;
 | |
| import java.net.*;
 | |
| import java.util.*;
 | |
| 
 | |
| import java.awt.Image;
 | |
| 
 | |
| /**
 | |
|  * Class representing the gtk+ clipboard selection. This is used when
 | |
|  * another program owns the clipboard. Whenever the system clipboard
 | |
|  * selection changes we create a new instance to notify the program
 | |
|  * that the available flavors might have changed. When requested it
 | |
|  * (lazily) caches the targets, and (text, image, or files/uris)
 | |
|  * clipboard contents.
 | |
|  */
 | |
| public class GtkSelection implements Transferable
 | |
| {
 | |
|   /**
 | |
|    * Static lock used for requests of mimetypes and contents retrieval.
 | |
|    */
 | |
|   static private Object requestLock = new Object();
 | |
| 
 | |
|   /**
 | |
|    * Whether we belong to the Clipboard (true) or to the Primary selection.
 | |
|    */
 | |
|   private final boolean clipboard;
 | |
| 
 | |
|   /**
 | |
|    * Whether a request for mimetypes, text, images, uris or byte[] is
 | |
|    * currently in progress. Should only be tested or set with
 | |
|    * requestLock held. When true no other requests should be made till
 | |
|    * it is false again.
 | |
|    */
 | |
|   private boolean requestInProgress;
 | |
| 
 | |
|   /**
 | |
|    * Indicates a requestMimeTypes() call was made and the
 | |
|    * corresponding mimeTypesAvailable() callback was triggered.
 | |
|    */
 | |
|   private boolean mimeTypesDelivered;
 | |
| 
 | |
|   /**
 | |
|    * Set and returned by getTransferDataFlavors. Only valid when
 | |
|    * mimeTypesDelivered is true.
 | |
|    */
 | |
|   private DataFlavor[] dataFlavors;
 | |
| 
 | |
|   /**
 | |
|    * Indicates a requestText() call was made and the corresponding
 | |
|    * textAvailable() callback was triggered.
 | |
|    */
 | |
|   private boolean textDelivered;
 | |
| 
 | |
|   /**
 | |
|    * Set as response to a requestText() call and possibly returned by
 | |
|    * getTransferData() for text targets. Only valid when textDelivered
 | |
|    * is true.
 | |
|    */
 | |
|   private String text;
 | |
| 
 | |
|   /**
 | |
|    * Indicates a requestImage() call was made and the corresponding
 | |
|    * imageAvailable() callback was triggered.
 | |
|    */
 | |
|   private boolean imageDelivered;
 | |
| 
 | |
|   /**
 | |
|    * Set as response to a requestImage() call and possibly returned by
 | |
|    * getTransferData() for image targets. Only valid when
 | |
|    * imageDelivered is true and image is null.
 | |
|    */
 | |
|   private Pointer imagePointer;
 | |
| 
 | |
|   /**
 | |
|    * Cached image value. Only valid when imageDelivered is
 | |
|    * true. Created from imagePointer.
 | |
|    */
 | |
|   private Image image;
 | |
| 
 | |
|   /**
 | |
|    * Indicates a requestUris() call was made and the corresponding
 | |
|    * urisAvailable() callback was triggered.
 | |
|    */
 | |
|   private boolean urisDelivered;
 | |
| 
 | |
|   /**
 | |
|    * Set as response to a requestURIs() call. Only valid when
 | |
|    * urisDelivered is true
 | |
|    */
 | |
|   private List<File> uris;
 | |
| 
 | |
|   /**
 | |
|    * Indicates a requestBytes(String) call was made and the
 | |
|    * corresponding bytesAvailable() callback was triggered.
 | |
|    */
 | |
|   private boolean bytesDelivered;
 | |
| 
 | |
|   /**
 | |
|    * Set as response to a requestBytes(String) call. Only valid when
 | |
|    * bytesDelivered is true.
 | |
|    */
 | |
|   private byte[] bytes;
 | |
| 
 | |
|   /**
 | |
|    * Should only be created by the GtkClipboard class. The clipboard
 | |
|    * should be either GtkClipboard.clipboard or
 | |
|    * GtkClipboard.selection.
 | |
|    */
 | |
|   GtkSelection(GtkClipboard clipboard)
 | |
|   {
 | |
|     this.clipboard = (clipboard == GtkClipboard.clipboard);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Gets an array of mime-type strings from the gtk+ clipboard and
 | |
|    * transforms them into an array of DataFlavors.
 | |
|    */
 | |
|   public DataFlavor[] getTransferDataFlavors()
 | |
|   {
 | |
|     DataFlavor[] result;
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         // Did we request already and cache the result?
 | |
|         if (mimeTypesDelivered)
 | |
|           result = (DataFlavor[]) dataFlavors.clone();
 | |
|         else
 | |
|           {
 | |
|             // Wait till there are no pending requests.
 | |
|             while (requestInProgress)
 | |
|               {
 | |
|                 try
 | |
|                   {
 | |
|                     requestLock.wait();
 | |
|                   }
 | |
|                 catch (InterruptedException ie)
 | |
|                   {
 | |
|                     // ignored
 | |
|                   }
 | |
|               }
 | |
| 
 | |
|             // If nobody else beat us and cached the result we try
 | |
|             // ourselves to get it.
 | |
|             if (! mimeTypesDelivered)
 | |
|               {
 | |
|                 requestInProgress = true;
 | |
|                 requestMimeTypes(clipboard);
 | |
|                 while (! mimeTypesDelivered)
 | |
|                   {
 | |
|                     try
 | |
|                       {
 | |
|                         requestLock.wait();
 | |
|                       }
 | |
|                     catch (InterruptedException ie)
 | |
|                       {
 | |
|                         // ignored
 | |
|                       }
 | |
|                   }
 | |
|                 requestInProgress = false;
 | |
|               }
 | |
|             result = dataFlavors;
 | |
|             if (! GtkClipboard.canCache)
 | |
|               {
 | |
|                 dataFlavors = null;
 | |
|                 mimeTypesDelivered = false;
 | |
|               }
 | |
|             requestLock.notifyAll();
 | |
|           }
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Callback that sets the available DataFlavors[]. Note that this
 | |
|    * should not call any code that could need the main gdk lock.
 | |
|    */
 | |
|   private void mimeTypesAvailable(String[] mimeTypes)
 | |
|   {
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         if (mimeTypes == null)
 | |
|           dataFlavors = new DataFlavor[0];
 | |
|         else
 | |
|           {
 | |
|             // Most likely the mimeTypes include text in which case we add an
 | |
|             // extra element.
 | |
|             ArrayList<DataFlavor> flavorsList =
 | |
|               new ArrayList<DataFlavor>(mimeTypes.length + 1);
 | |
| 
 | |
|             for (int i = 0; i < mimeTypes.length; i++)
 | |
|               {
 | |
|                 try
 | |
|                   {
 | |
|                     if (mimeTypes[i] == GtkClipboard.stringMimeType)
 | |
|                       {
 | |
|                         // XXX - Fix DataFlavor.getTextPlainUnicodeFlavor()
 | |
|                         // and also add it to the list.
 | |
|                         flavorsList.add(DataFlavor.stringFlavor);
 | |
|                         flavorsList.add(DataFlavor.plainTextFlavor);
 | |
|                       }
 | |
|                     else if (mimeTypes[i] == GtkClipboard.imageMimeType)
 | |
|                       flavorsList.add(DataFlavor.imageFlavor);
 | |
|                     else if (mimeTypes[i] == GtkClipboard.filesMimeType)
 | |
|                       flavorsList.add(DataFlavor.javaFileListFlavor);
 | |
|                     else
 | |
|                       {
 | |
|                         // We check the target to prevent duplicates
 | |
|                         // of the "magic" targets above.
 | |
|                         DataFlavor target = new DataFlavor(mimeTypes[i]);
 | |
|                         if (! flavorsList.contains(target))
 | |
|                           flavorsList.add(target);
 | |
|                       }
 | |
|                   }
 | |
|                 catch (ClassNotFoundException cnfe)
 | |
|                   {
 | |
|                     cnfe.printStackTrace();
 | |
|                   }
 | |
|                 catch (NullPointerException npe)
 | |
|                   {
 | |
|                     npe.printStackTrace();
 | |
|                   }
 | |
|               }
 | |
| 
 | |
|             dataFlavors = new DataFlavor[flavorsList.size()];
 | |
|             flavorsList.toArray(dataFlavors);
 | |
|           }
 | |
| 
 | |
|         mimeTypesDelivered = true;
 | |
|         requestLock.notifyAll();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Gets the available data flavors for this selection and checks
 | |
|    * that at least one of them is equal to the given DataFlavor.
 | |
|    */
 | |
|   public boolean isDataFlavorSupported(DataFlavor flavor)
 | |
|   {
 | |
|     DataFlavor[] dfs = getTransferDataFlavors();
 | |
|     for (int i = 0; i < dfs.length; i++)
 | |
|       if (flavor.equals(dfs[i]))
 | |
|         return true;
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Helper method that tests whether we already have the text for the
 | |
|    * current gtk+ selection on the clipboard and if not requests it
 | |
|    * and waits till it is available.
 | |
|    */
 | |
|   private String getText()
 | |
|   {
 | |
|     String result;
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         // Did we request already and cache the result?
 | |
|         if (textDelivered)
 | |
|           result = text;
 | |
|         else
 | |
|           {
 | |
|             // Wait till there are no pending requests.
 | |
|             while (requestInProgress)
 | |
|               {
 | |
|                 try
 | |
|                   {
 | |
|                     requestLock.wait();
 | |
|                   }
 | |
|                 catch (InterruptedException ie)
 | |
|                   {
 | |
|                     // ignored
 | |
|                   }
 | |
|               }
 | |
| 
 | |
|             // If nobody else beat us we try ourselves to get and
 | |
|             // caching the result.
 | |
|             if (! textDelivered)
 | |
|               {
 | |
|                 requestInProgress = true;
 | |
|                 requestText(clipboard);
 | |
|                 while (! textDelivered)
 | |
|                   {
 | |
|                     try
 | |
|                       {
 | |
|                         requestLock.wait();
 | |
|                       }
 | |
|                     catch (InterruptedException ie)
 | |
|                       {
 | |
|                         // ignored
 | |
|                       }
 | |
|                   }
 | |
|                 requestInProgress = false;
 | |
|               }
 | |
|             result = text;
 | |
|             if (! GtkClipboard.canCache)
 | |
|               {
 | |
|                 text = null;
 | |
|                 textDelivered = false;
 | |
|               }
 | |
|             requestLock.notifyAll();
 | |
|           }
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Callback that sets the available text on the clipboard. Note that
 | |
|    * this should not call any code that could need the main gdk lock.
 | |
|    */
 | |
|   private void textAvailable(String text)
 | |
|   {
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         this.text = text;
 | |
|         textDelivered = true;
 | |
|         requestLock.notifyAll();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Helper method that tests whether we already have an image for the
 | |
|    * current gtk+ selection on the clipboard and if not requests it
 | |
|    * and waits till it is available.
 | |
|    */
 | |
|   private Image getImage()
 | |
|   {
 | |
|     Image result;
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         // Did we request already and cache the result?
 | |
|         if (imageDelivered)
 | |
|           result = image;
 | |
|         else
 | |
|           {
 | |
|             // Wait till there are no pending requests.
 | |
|             while (requestInProgress)
 | |
|               {
 | |
|                 try
 | |
|                   {
 | |
|                     requestLock.wait();
 | |
|                   }
 | |
|                 catch (InterruptedException ie)
 | |
|                   {
 | |
|                     // ignored
 | |
|                   }
 | |
|               }
 | |
| 
 | |
|             // If nobody else beat us we try ourselves to get and
 | |
|             // caching the result.
 | |
|             if (! imageDelivered)
 | |
|               {
 | |
|                 requestInProgress = true;
 | |
|                 requestImage(clipboard);
 | |
|                 while (! imageDelivered)
 | |
|                   {
 | |
|                     try
 | |
|                       {
 | |
|                         requestLock.wait();
 | |
|                       }
 | |
|                     catch (InterruptedException ie)
 | |
|                       {
 | |
|                         // ignored
 | |
|                       }
 | |
|                   }
 | |
|                 requestInProgress = false;
 | |
|               }
 | |
| 
 | |
|             if (imagePointer != null)
 | |
|               image = new GtkImage(imagePointer);
 | |
| 
 | |
|             imagePointer = null;
 | |
|             result = image;
 | |
|             if (! GtkClipboard.canCache)
 | |
|               {
 | |
|                 image = null;
 | |
|                 imageDelivered = false;
 | |
|               }
 | |
|             requestLock.notifyAll();
 | |
|           }
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Callback that sets the available image on the clipboard. Note
 | |
|    * that this should not call any code that could need the main gdk
 | |
|    * lock. Note that we get a Pointer to a GdkPixbuf which we cannot
 | |
|    * turn into a real GtkImage at this point. That will be done on the
 | |
|    * "user thread" in getImage().
 | |
|    */
 | |
|   private void imageAvailable(Pointer pointer)
 | |
|   {
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         this.imagePointer = pointer;
 | |
|         imageDelivered = true;
 | |
|         requestLock.notifyAll();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Helper method that test whether we already have a list of
 | |
|    * URIs/Files and if not requests them and waits till they are
 | |
|    * available.
 | |
|    */
 | |
|   private List<File> getURIs()
 | |
|   {
 | |
|     List<File> result;
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         // Did we request already and cache the result?
 | |
|         if (urisDelivered)
 | |
|           result = uris;
 | |
|         else
 | |
|           {
 | |
|             // Wait till there are no pending requests.
 | |
|             while (requestInProgress)
 | |
|               {
 | |
|                 try
 | |
|                   {
 | |
|                     requestLock.wait();
 | |
|                   }
 | |
|                 catch (InterruptedException ie)
 | |
|                   {
 | |
|                     // ignored
 | |
|                   }
 | |
|               }
 | |
| 
 | |
|             // If nobody else beat us we try ourselves to get and
 | |
|             // caching the result.
 | |
|             if (! urisDelivered)
 | |
|               {
 | |
|                 requestInProgress = true;
 | |
|                 requestURIs(clipboard);
 | |
|                 while (! urisDelivered)
 | |
|                   {
 | |
|                     try
 | |
|                       {
 | |
|                         requestLock.wait();
 | |
|                       }
 | |
|                     catch (InterruptedException ie)
 | |
|                       {
 | |
|                         // ignored
 | |
|                       }
 | |
|                   }
 | |
|                 requestInProgress = false;
 | |
|               }
 | |
|             result = uris;
 | |
|             if (! GtkClipboard.canCache)
 | |
|               {
 | |
|                 uris = null;
 | |
|                 urisDelivered = false;
 | |
|               }
 | |
|             requestLock.notifyAll();
 | |
|           }
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Callback that sets the available File list. Note that this should
 | |
|    * not call any code that could need the main gdk lock.
 | |
|    */
 | |
|   private void urisAvailable(String[] uris)
 | |
|   {
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         if (uris != null && uris.length != 0)
 | |
|           {
 | |
|             ArrayList<File> list = new ArrayList<File>(uris.length);
 | |
|             for (int i = 0; i < uris.length; i++)
 | |
|               {
 | |
|                 try
 | |
|                   {
 | |
|                     URI uri = new URI(uris[i]);
 | |
|                     if (uri.getScheme().equals("file"))
 | |
|                       list.add(new File(uri));
 | |
|                   }
 | |
|                 catch (URISyntaxException use)
 | |
|                   {
 | |
|                   }
 | |
|               }
 | |
|             this.uris = list;
 | |
|           }
 | |
| 
 | |
|         urisDelivered = true;
 | |
|         requestLock.notifyAll();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Helper method that requests a byte[] for the given target
 | |
|    * mime-type flavor and waits till it is available. Note that unlike
 | |
|    * the other get methods this one doesn't cache the result since
 | |
|    * there are possibly many targets.
 | |
|    */
 | |
|   private byte[] getBytes(String target)
 | |
|   {
 | |
|     byte[] result;
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         // Wait till there are no pending requests.
 | |
|         while (requestInProgress)
 | |
|           {
 | |
|             try
 | |
|               {
 | |
|                 requestLock.wait();
 | |
|               }
 | |
|             catch (InterruptedException ie)
 | |
|               {
 | |
|                 // ignored
 | |
|               }
 | |
|           }
 | |
| 
 | |
|         // Request bytes and wait till they are available.
 | |
|         requestInProgress = true;
 | |
|         requestBytes(clipboard, target);
 | |
|         while (! bytesDelivered)
 | |
|           {
 | |
|             try
 | |
|               {
 | |
|                 requestLock.wait();
 | |
|               }
 | |
|             catch (InterruptedException ie)
 | |
|               {
 | |
|                 // ignored
 | |
|               }
 | |
|           }
 | |
|         result = bytes;
 | |
|         bytes = null;
 | |
|         bytesDelivered = false;
 | |
|         requestInProgress = false;
 | |
| 
 | |
|         requestLock.notifyAll();
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Callback that sets the available byte array on the
 | |
|    * clipboard. Note that this should not call any code that could
 | |
|    * need the main gdk lock.
 | |
|    */
 | |
|   private void bytesAvailable(byte[] bytes)
 | |
|   {
 | |
|     synchronized (requestLock)
 | |
|       {
 | |
|         this.bytes = bytes;
 | |
|         bytesDelivered = true;
 | |
|         requestLock.notifyAll();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   public Object getTransferData(DataFlavor flavor)
 | |
|     throws UnsupportedFlavorException
 | |
|   {
 | |
|     // Note the fall throughs for the "magic targets" if they fail we
 | |
|     // try one more time through getBytes().
 | |
|     if (flavor.equals(DataFlavor.stringFlavor))
 | |
|       {
 | |
|         String text = getText();
 | |
|         if (text != null)
 | |
|           return text;
 | |
|       }
 | |
| 
 | |
|     if (flavor.equals(DataFlavor.plainTextFlavor))
 | |
|       {
 | |
|         String text = getText();
 | |
|         if (text != null)
 | |
|           return new StringBufferInputStream(text);
 | |
|       }
 | |
| 
 | |
|     if (flavor.equals(DataFlavor.imageFlavor))
 | |
|       {
 | |
|         Image image = getImage();
 | |
|         if (image != null)
 | |
|           return image;
 | |
|       }
 | |
| 
 | |
|     if (flavor.equals(DataFlavor.javaFileListFlavor))
 | |
|       {
 | |
|         List<File> uris = getURIs();
 | |
|         if (uris != null)
 | |
|           return uris;
 | |
|       }
 | |
| 
 | |
|     byte[] bytes = getBytes(flavor.getMimeType());
 | |
|     if (bytes == null)
 | |
|       throw new UnsupportedFlavorException(flavor);
 | |
| 
 | |
|     if (flavor.isMimeTypeSerializedObject())
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
 | |
|             ObjectInputStream ois = new ObjectInputStream(bais);
 | |
|             return ois.readObject();
 | |
|           }
 | |
|         catch (IOException ioe)
 | |
|           {
 | |
|             ioe.printStackTrace();
 | |
|           }
 | |
|         catch (ClassNotFoundException cnfe)
 | |
|           {
 | |
|             cnfe.printStackTrace();
 | |
|           }
 | |
|       }
 | |
| 
 | |
|     if (flavor.isRepresentationClassInputStream())
 | |
|       return new ByteArrayInputStream(bytes);
 | |
| 
 | |
|     // XXX, need some more conversions?
 | |
| 
 | |
|     throw new UnsupportedFlavorException(flavor);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Requests text, Image or an byte[] for a particular target from the
 | |
|    * other application. These methods return immediately. When the
 | |
|    * content is available the contentLock will be notified through
 | |
|    * textAvailable, imageAvailable, urisAvailable or bytesAvailable and the
 | |
|    * appropriate field is set.
 | |
|    * The clipboard argument is true if we want the Clipboard, and false
 | |
|    * if we want the (primary) selection.
 | |
|    */
 | |
|   private native void requestText(boolean clipboard);
 | |
|   private native void requestImage(boolean clipboard);
 | |
|   private native void requestURIs(boolean clipboard);
 | |
|   private native void requestBytes(boolean clipboard, String target);
 | |
| 
 | |
|   /* Similar to the above but for requesting the supported targets. */
 | |
|   private native void requestMimeTypes(boolean clipboard);
 | |
| }
 |