classpath-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cp-patches] Reimplemented gtk-peers datatransfer support


From: Mark Wielaard
Subject: [cp-patches] Reimplemented gtk-peers datatransfer support
Date: Tue, 16 Aug 2005 20:00:04 +0200

Hi,

This is a complete reimplementation of the gtk-peers datatransfer
support. 

2005-08-16  Mark Wielaard  <address@hidden>

    * gnu/java/awt/peer/gtk/GtkClipboard.java: Reimplemented.
    * gnu/java/awt/peer/gtk/GtkClipboardNotifier.java: New class.
    * gnu/java/awt/peer/gtk/GtkSelection.java: New class.
    * gnu/java/awt/peer/gtk/GtkImage.java (GtkImage(Pointer)): New
    constructor.
    (createFromPixbuf): New private native method.
    * gnu/java/awt/peer/gtk/GtkToolkit.java (systemClipboard): Removed.
    (getSystemClipboard): Do security check and return
    GtkClipboard.getInstance().
    * include/Makefile.am: Add gnu_java_awt_peer_gtk_GtkSelection.h.
    * include/gnu_java_awt_peer_gtk_GtkClipboard.h: Regenerated.
    * include/gnu_java_awt_peer_gtk_GtkImage.h: Regenerated.
    * include/gnu_java_awt_peer_gtk_GtkSelection.h: New header file.
    * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c:
    Reimplemented.
    * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c
    (Java_gnu_java_awt_peer_gtk_GtkImage_createFromPixbuf): New

It has support for the standard "magic targets" text, images, file lists
and serialized objects. Where possible it uses the gtk+ clipboard and
selections support to transparently translate between different target
mime-types.

I would like for someone to review how I handled the native/gtk
lock/thread transitions. Since the datatransfer api is basically
synchronous, but the gtkclipboard is asynchronous we need to call into
gtk+ to setup callbacks and then wait till those trigger before we can
return the results to the user. This is slightly complicated by the fact
that only one outstanding selection request is allowed. Where possible
the code tries to sensibly cache the results of any datatransformation
it has done.

When gtk+ calls us to inform that the system clipboard has changed we
want to set a new GtkSelection facade object on the clipboard. But since
this can trigger Owner and Flavor listeners we only set a flag and
notify a new GtkSelectionNotifier deamon thread to do this for us since
during the callback we are holding the main gtk lock. Maybe we can use
the EventQueue.invokeLater() trick for this so we don't need an extra
thread just for clipboard owner-change notifications.

There is one known issue and that is with the
GtkClipboard.provideContent() methods. They are called from a callback
(and are holding the main gtk lock) but call arbitrary user code through
DataTransfer.getTransferData(). This could potentially deadlock if that
user code somehow calls back into gtk+. To solve this properly we need a
change in the gtk+ clipboard/selection code to allow us to provide the
data asynchronous.

I have committed it already since it is a huge improvement over the old
(buggy, non-working) code. If you want to play with it try the new
datatransfer demo example gnu.classpath.examples.datatransfer.Demo.
Comments on coding style, rants on lock/threading issues or other issues
you can find in the code are appreciated.

There is still some other cleanup to do in the main awt.datatransfer
code. Mostly in DataFlavor. I hope to do that soon.

Cheers,

Mark
Index: gnu/java/awt/peer/gtk/GtkClipboard.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java,v
retrieving revision 1.9
diff -u -r1.9 GtkClipboard.java
--- gnu/java/awt/peer/gtk/GtkClipboard.java     2 Jul 2005 20:32:12 -0000       
1.9
+++ gnu/java/awt/peer/gtk/GtkClipboard.java     16 Aug 2005 17:29:16 -0000
@@ -38,133 +38,357 @@
 
 package gnu.java.awt.peer.gtk;
 
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.ClipboardOwner;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.awt.Image;
+import java.awt.datatransfer.*;
+
+import java.io.*;
+
+import java.util.List;
+import java.util.Iterator;
 
 public class GtkClipboard extends Clipboard
 {
-  /* the number of milliseconds that we'll wait around for the
-     owner of the GDK_SELECTION_PRIMARY selection to convert 
-     the requested data */
-  static final int SELECTION_RECEIVED_TIMEOUT = 5000;
-
-  /* We currently only support transferring of text between applications */
-  static String selection;
-  static Object selectionLock = new Object ();
 
-  static boolean hasSelection = false;
+  // Given to the native side so it can signal special targets that
+  // can be converted to one of the special predefined DataFlavors.
+  static final String stringMimeType;
+  static final String imageMimeType;
+  static final String filesMimeType;
+
+  // Indicates whether the results of the clipboard selection can be
+  // cached by GtkSelection. True if
+  // gdk_display_supports_selection_notification.
+  static final boolean canCache;
+
+  static
+  {
+    stringMimeType = DataFlavor.stringFlavor.getMimeType();
+    imageMimeType = DataFlavor.imageFlavor.getMimeType();
+    filesMimeType = DataFlavor.javaFileListFlavor.getMimeType();
+
+    canCache = initNativeState(stringMimeType, imageMimeType, filesMimeType);
+  }
 
-  protected GtkClipboard()
+  /**
+   * The one and only gtk+ clipboard instance.
+   */
+  private static GtkClipboard instance = new GtkClipboard();
+
+  /**
+   * Creates the clipboard and sets the initial contents to the
+   * current gtk+ selection.
+   */
+  private GtkClipboard()
   {
     super("System Clipboard");
-    initNativeState();
+    setContents(new GtkSelection(), null);
   }
 
-  public Transferable getContents(Object requestor)
+  /**
+   * Returns the one and only GtkClipboard instance.
+   */
+
+  static GtkClipboard getInstance()
   {
-    synchronized (this)
+    return instance;
+  }
+
+  /**
+   * Sets the GtkSelection facade as new contents of the clipboard.
+   * Called from gtk+ when another application grabs the clipboard and
+   * we loose ownership.
+   */
+  private static void setSystemContents()
+  {
+    GtkClipboardNotifier.announce();
+  }
+
+  /**
+   * Sets the new contents and advertises the available flavors to the
+   * gtk+ clipboard.
+   */
+  public synchronized void setContents(Transferable contents,
+                                      ClipboardOwner owner)
+  {
+    super.setContents(contents, owner);
+
+    if (contents == null)
       {
-       if (hasSelection)
-         return contents;
+       advertiseContent(null, false, false, false);
+       return;
       }
 
-    /* Java doesn't own the selection, so we need to ask X11 */
-    // XXX: Does this hold with Swing too ?
-    synchronized (selectionLock)
+    // We don't need to do anything for a GtkSelection facade.
+    if (contents instanceof GtkSelection)
+      return;
+
+    boolean text = false;
+    boolean images = false;
+    boolean files = false;
+
+    if (contents instanceof StringSelection
+       || contents.isDataFlavorSupported(DataFlavor.stringFlavor)
+       || contents.isDataFlavorSupported(DataFlavor.plainTextFlavor)
+       || contents.isDataFlavorSupported(DataFlavor
+                                         .getTextPlainUnicodeFlavor()))
+      text = true;
+
+    DataFlavor[] flavors = contents.getTransferDataFlavors();
+    String[] mimeTargets = new String[flavors.length];
+    for (int i = 0; i < flavors.length; i++)
       {
-       requestStringConversion();
-       
-       try 
+       DataFlavor flavor = flavors[i];
+       String mimeType = flavor.getMimeType();
+       mimeTargets[i] = mimeType;
+
+       if (! text)
+         if ("text".equals(flavor.getPrimaryType())
+             || flavor.isRepresentationClassReader())
+           text = true;
+
+       // XXX - We only support automatic image conversion for
+       // GtkImages at the moment. So explicitly check that we have
+       // one.
+       if (! images && flavors[i].equals(DataFlavor.imageFlavor))
          {
-           selectionLock.wait(SELECTION_RECEIVED_TIMEOUT);
-         } 
-       catch (InterruptedException e)
-         {
-           return null;
+           try
+             {
+               Object o = contents.getTransferData(DataFlavor.imageFlavor);
+               if (o instanceof GtkImage)
+                 images = true;
+             }
+           catch (UnsupportedFlavorException ufe)
+             {
+             }
+           catch (IOException ioe)
+             {
+             }
+           catch (ClassCastException cce)
+             {
+             }
          }
-       
-       return selection == null ? null : new StringSelection(selection);
+
+       if (flavors[i].equals(DataFlavor.javaFileListFlavor))
+         files = true;
       }
+
+    advertiseContent(mimeTargets, text, images, files);
   }
 
-  void stringSelectionReceived(String newSelection)
+  /**
+   * Advertises new contents to the gtk+ clipboard given a string
+   * array of (mime-type) targets. When the boolean flags text, images
+   * and/or files are set then gtk+ is asked to also advertise the
+   * availability of any text, image or uri/file content types it
+   * supports. If targets is null (and all flags false) then the
+   * selection has explicitly been erased.
+   */
+  private native void advertiseContent(String[] targets,
+                                      boolean text,
+                                      boolean images,
+                                      boolean files);
+  
+  /**
+   * Called by the gtk+ clipboard when an application has requested
+   * text.  Return a string representing the current clipboard
+   * contents or null when no text can be provided.
+   */
+  private String provideText()
   {
-    synchronized (selectionLock)
+    Transferable contents = this.contents;
+    if (contents == null || contents instanceof GtkSelection)
+      return null;
+
+    // Handle StringSelection special since that is just pure text.
+    if (contents instanceof StringSelection)
       {
-       selection = newSelection;
-       selectionLock.notify();
+        try
+          {
+            return (String) contents.getTransferData(DataFlavor.stringFlavor);
+         }
+        catch (UnsupportedFlavorException ufe)
+          {
+          }
+        catch (IOException ioe)
+          {
+          }
+        catch (ClassCastException cce)
+          {
+          }
       }
-  }
-
-  /* convert Java clipboard data into a String suitable for sending
-     to another application */
-  synchronized String stringSelectionHandler() throws IOException
-  {
-    String selection = null;
 
+    // Try to get a plain text reader for the current contents and
+    // turn the result into a string.
     try
       {
-       if (contents.isDataFlavorSupported(DataFlavor.stringFlavor))
-         selection = (String)contents.getTransferData(DataFlavor.stringFlavor);
-       else if (contents.isDataFlavorSupported(DataFlavor.plainTextFlavor))
+       DataFlavor plainText = DataFlavor.getTextPlainUnicodeFlavor();
+       Reader r = plainText.getReaderForText(contents);
+       if (r != null)
          {
-           StringBuffer sbuf = new StringBuffer();
-           InputStreamReader reader;
-           char readBuf[] = new char[512];
-           int numChars;
-         
-           reader = new InputStreamReader 
-             ((InputStream) 
-              contents.getTransferData(DataFlavor.plainTextFlavor), "UNICODE");
-         
-           while (true)
-             {
-               numChars = reader.read(readBuf);
-               if (numChars == -1)
-                 break;
-               sbuf.append(readBuf, 0, numChars);
+           StringBuffer sb = new StringBuffer();
+           char[] cs = new char[1024];
+           int l = r.read(cs);
+           while (l != -1)
+             {
+               sb.append(cs, 0, l);
+               l = r.read(cs);
              }
-         
-           selection = new String(sbuf);
+           return sb.toString();
          }
       }
-    catch (Exception e)
+    catch (IllegalArgumentException iae)
+      {
+      }
+    catch (UnsupportedEncodingException iee)
+      {
+      }
+    catch (UnsupportedFlavorException ufe)
       {
       }
-    
-    return selection;
+    catch (IOException ioe)
+      {
+      }
+
+    return null;
   }
 
-  public synchronized void setContents(Transferable contents,
-                                      ClipboardOwner owner)
+  /**
+   * Called by the gtk+ clipboard when an application has requested an
+   * image.  Returns a GtkImage representing the current clipboard
+   * contents or null when no image can be provided.
+   */
+  private GtkImage provideImage()
   {
-    selectionGet();
+    Transferable contents = this.contents;
+    if (contents == null || contents instanceof GtkSelection)
+      return null;
 
-    this.contents = contents;
-    this.owner = owner;
+    try
+      {
+       return (GtkImage) contents.getTransferData(DataFlavor.imageFlavor);
+      }
+    catch (UnsupportedFlavorException ufe)
+      {
+      }
+    catch (IOException ioe)
+      {
+      }
+    catch (ClassCastException cce)
+      {
+      }
 
-    hasSelection = true;
+    return null;
   }
 
-  synchronized void selectionClear()
+  /**
+   * Called by the gtk+ clipboard when an application has requested a
+   * uri-list.  Return a string array containing the URIs representing
+   * the current clipboard contents or null when no URIs can be
+   * provided.
+   */
+  private String[] provideURIs()
   {
-    hasSelection = false;
+    Transferable contents = this.contents;
+    if (contents == null || contents instanceof GtkSelection)
+      return null;
 
-    if (owner != null)
+    try
       {
-       owner.lostOwnership(this, contents);
-       owner = null;
-       contents = null;
+       List list = (List) contents.getTransferData
+         (DataFlavor.javaFileListFlavor);
+       String[] uris = new String[list.size()];
+       int u = 0;
+       Iterator it = list.iterator();
+       while (it.hasNext())
+         uris[u++] = ((File) it.next()).toURI().toString();
+       return uris;
       }
+    catch (UnsupportedFlavorException ufe)
+      {
+      }
+    catch (IOException ioe)
+      {
+      }
+    catch (ClassCastException cce)
+      {
+      }
+
+    return null;
+  }
+
+  /**
+   * Called by gtk+ clipboard when an application requests the given
+   * target mime-type. Returns a byte array containing the requested
+   * data, or null when the contents cannot be provided in the
+   * requested target mime-type. Only called after any explicit text,
+   * image or file/uri requests have been handled earlier and failed.
+   */
+  private byte[] provideContent(String target)
+  {
+    // Sanity check. The callback could be triggered just after we
+    // changed the clipboard.
+    Transferable contents = this.contents;
+    if (contents == null || contents instanceof GtkSelection)
+      return null;
+
+    // XXX - We are being called from a gtk+ callback. Which means we
+    // should return as soon as possible and not call arbitrary code
+    // that could deadlock or go bonkers. But we don't really know
+    // what DataTransfer contents object we are dealing with. Same for
+    // the other provideXXX() methods.
+    try
+      {
+       DataFlavor flavor = new DataFlavor(target);
+       Object o = contents.getTransferData(flavor);
+
+       if (o instanceof byte[])
+         return (byte[]) o;
+
+       if (o instanceof InputStream)
+         {
+           InputStream is = (InputStream) o;
+           ByteArrayOutputStream baos = new ByteArrayOutputStream();
+           byte[] bs = new byte[1024];
+           int l = is.read(bs);
+           while (l != -1)
+             {
+               baos.write(bs, 0, l);
+               l = is.read(bs);
+             }
+           return baos.toByteArray();
+         }
+
+       if (o instanceof Serializable)
+         {
+           ByteArrayOutputStream baos = new ByteArrayOutputStream();
+           ObjectOutputStream oos = new ObjectOutputStream(baos);
+           oos.writeObject(o);
+           oos.close();
+           return baos.toByteArray();
+         }
+      }
+    catch (ClassNotFoundException cnfe)
+      {
+      }
+    catch (UnsupportedFlavorException ufe)
+      {
+      }
+    catch (IOException ioe)
+      {
+      }
+    catch (ClassCastException cce)
+      {
+      }
+
+    return null;
   }
 
-  native void initNativeState();
-  static native void requestStringConversion();
-  static native void selectionGet();
+  /**
+   * Initializes the gtk+ clipboard and caches any native side
+   * structures needed. Returns whether or not the contents of the
+   * Clipboard can be cached (gdk_display_supports_selection_notification).
+   */
+  private static native boolean initNativeState(String stringTarget,
+                                               String imageTarget,
+                                               String filesTarget);
 }
Index: gnu/java/awt/peer/gtk/GtkClipboardNotifier.java
===================================================================
RCS file: gnu/java/awt/peer/gtk/GtkClipboardNotifier.java
diff -N gnu/java/awt/peer/gtk/GtkClipboardNotifier.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/java/awt/peer/gtk/GtkClipboardNotifier.java     16 Aug 2005 17:29:16 
-0000
@@ -0,0 +1,112 @@
+/* GtkClipboardNotifier.java -- Helper for announcing GtkSelection changes.
+   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 java.awt.datatransfer.*;
+
+class GtkClipboardNotifier extends Thread
+{
+  /** Whether or not to announce a GtkSelection change. */
+  private static boolean announceChange;
+
+  /**
+   * The one and only instance. All operations are synchronized on
+   * this.
+   */
+  private static GtkClipboardNotifier notifier = new GtkClipboardNotifier();
+
+  /**
+   * Creates a deamon thread that monitors this for change
+   * announcements.
+   */
+  private GtkClipboardNotifier()
+  {
+    super("GtkClipBoardNotifier");
+    setDaemon(true);
+    start();
+  }
+
+  /**
+   * Notifies that a new GtkSelection has to be announced.
+   */
+  static void announce()
+  {
+    synchronized (notifier)
+      {
+       announceChange = true;
+       notifier.notifyAll();
+      }
+  }
+
+  public void run()
+  {
+    final GtkClipboard clipboard = GtkClipboard.getInstance();
+    while (true)
+      {
+       synchronized (this)
+         {
+           while (!announceChange)
+             {
+               try
+                 {
+                   this.wait();
+                 }
+               catch (InterruptedException ie)
+                 {
+                   // ignore
+                 }
+             }
+           announceChange = false;
+         }
+
+       // Do the actual announcement without the lock held.  We will
+       // notice a new change after this notification has finished.
+       try
+         {
+           clipboard.setContents(new GtkSelection(), null);
+         }
+       catch (Throwable t)
+         {
+           // should never happen, but might if we have some faulty
+           // listener.
+           t.printStackTrace();
+         }
+      }
+  }
+}
Index: gnu/java/awt/peer/gtk/GtkImage.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkImage.java,v
retrieving revision 1.22
diff -u -r1.22 GtkImage.java
--- gnu/java/awt/peer/gtk/GtkImage.java 3 Aug 2005 13:12:59 -0000       1.22
+++ gnu/java/awt/peer/gtk/GtkImage.java 16 Aug 2005 17:29:16 -0000
@@ -241,6 +241,25 @@
   }
 
   /**
+   * Package private constructor to create a GtkImage from a given
+   * PixBuf pointer.
+   */
+  GtkImage (Pointer pixbuf)
+  {
+    pixmap = pixbuf;
+    createFromPixbuf();
+    isLoaded = true;
+    observers = null;
+    offScreen = false;
+    props = new Hashtable();
+  }
+
+  /**
+   * Native helper function for constructor that takes a pixbuf Pointer.
+   */
+  private native void createFromPixbuf();
+
+  /**
    * Callback from the image consumer.
    */
   public void setImage(int width, int height, 
Index: gnu/java/awt/peer/gtk/GtkSelection.java
===================================================================
RCS file: gnu/java/awt/peer/gtk/GtkSelection.java
diff -N gnu/java/awt/peer/gtk/GtkSelection.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/java/awt/peer/gtk/GtkSelection.java     16 Aug 2005 17:29:16 -0000
@@ -0,0 +1,664 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 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.
+ *
+ * XXX - should only cache when
+ * gdk_display_supports_selection_notification is true.
+ */
+public class GtkSelection implements Transferable
+{
+  /**
+   * Static lock used for requests of mimetypes and contents retrieval.
+   */
+  static private Object requestLock = new Object();
+
+  /**
+   * 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 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.
+   */
+  GtkSelection()
+  {
+  }
+
+  /**
+   * 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();
+               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 flavorsList = new ArrayList(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();
+               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();
+               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 getURIs()
+  {
+    List 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();
+               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 list = new ArrayList(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(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 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.
+   */
+  private native void requestText();
+  private native void requestImage();
+  private native void requestURIs();
+  private native void requestBytes(String target);
+
+  /* Similar to the above but for requesting the supported targets. */
+  private native void requestMimeTypes();
+}
Index: gnu/java/awt/peer/gtk/GtkToolkit.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java,v
retrieving revision 1.73
diff -u -r1.73 GtkToolkit.java
--- gnu/java/awt/peer/gtk/GtkToolkit.java       15 Aug 2005 18:32:38 -0000      
1.73
+++ gnu/java/awt/peer/gtk/GtkToolkit.java       16 Aug 2005 17:29:16 -0000
@@ -88,7 +88,6 @@
 {
   Hashtable containers = new Hashtable();
   static EventQueue q;
-  static Clipboard systemClipboard;
   static boolean useGraphics2dSet;
   static boolean useGraphics2d;
 
@@ -136,7 +135,6 @@
 
   public GtkToolkit ()
   {
-    systemClipboard = new GtkClipboard ();
   }
 
   public native void beep();
@@ -406,7 +404,11 @@
 
   public Clipboard getSystemClipboard() 
   {
-    return systemClipboard;
+    SecurityManager secman = System.getSecurityManager();
+    if (secman != null)
+      secman.checkSystemClipboardAccess();
+
+    return GtkClipboard.getInstance();
   }
 
   /**
Index: include/Makefile.am
===================================================================
RCS file: /cvsroot/classpath/classpath/include/Makefile.am,v
retrieving revision 1.45
diff -u -r1.45 Makefile.am
--- include/Makefile.am 5 Jul 2005 05:46:08 -0000       1.45
+++ include/Makefile.am 16 Aug 2005 17:29:16 -0000
@@ -57,6 +57,7 @@
 $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkPopupMenuPeer.h \
 $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkScrollPanePeer.h \
 $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkScrollbarPeer.h \
+$(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkSelection.h \
 $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkTextAreaPeer.h \
 $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkTextFieldPeer.h \
 $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkToolkit.h \
Index: include/gnu_java_awt_peer_gtk_GtkClipboard.h
===================================================================
RCS file: 
/cvsroot/classpath/classpath/include/gnu_java_awt_peer_gtk_GtkClipboard.h,v
retrieving revision 1.4
diff -u -r1.4 gnu_java_awt_peer_gtk_GtkClipboard.h
--- include/gnu_java_awt_peer_gtk_GtkClipboard.h        11 Nov 2004 17:31:31 
-0000      1.4
+++ include/gnu_java_awt_peer_gtk_GtkClipboard.h        16 Aug 2005 17:29:16 
-0000
@@ -10,11 +10,8 @@
 {
 #endif
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkClipboard_initNativeState 
(JNIEnv *env, jobject);
-JNIEXPORT void JNICALL 
Java_gnu_java_awt_peer_gtk_GtkClipboard_requestStringConversion (JNIEnv *env, 
jclass);
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkClipboard_selectionGet 
(JNIEnv *env, jclass);
-#undef gnu_java_awt_peer_gtk_GtkClipboard_SELECTION_RECEIVED_TIMEOUT
-#define gnu_java_awt_peer_gtk_GtkClipboard_SELECTION_RECEIVED_TIMEOUT 5000L
+JNIEXPORT void JNICALL 
Java_gnu_java_awt_peer_gtk_GtkClipboard_advertiseContent (JNIEnv *env, jobject, 
jobjectArray, jboolean, jboolean, jboolean);
+JNIEXPORT jboolean JNICALL 
Java_gnu_java_awt_peer_gtk_GtkClipboard_initNativeState (JNIEnv *env, jclass, 
jstring, jstring, jstring);
 
 #ifdef __cplusplus
 }
Index: include/gnu_java_awt_peer_gtk_GtkImage.h
===================================================================
RCS file: 
/cvsroot/classpath/classpath/include/gnu_java_awt_peer_gtk_GtkImage.h,v
retrieving revision 1.2
diff -u -r1.2 gnu_java_awt_peer_gtk_GtkImage.h
--- include/gnu_java_awt_peer_gtk_GtkImage.h    8 Jun 2005 21:19:25 -0000       
1.2
+++ include/gnu_java_awt_peer_gtk_GtkImage.h    16 Aug 2005 17:29:16 -0000
@@ -18,6 +18,7 @@
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkImage_createScaledPixmap 
(JNIEnv *env, jobject, jobject, jint);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkImage_drawPixelsScaled 
(JNIEnv *env, jobject, jobject, jint, jint, jint, jint, jint, jint, jint, 
jboolean);
 JNIEXPORT void JNICALL 
Java_gnu_java_awt_peer_gtk_GtkImage_drawPixelsScaledFlipped (JNIEnv *env, 
jobject, jobject, jint, jint, jint, jboolean, jboolean, jint, jint, jint, jint, 
jint, jint, jint, jint, jboolean);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkImage_createFromPixbuf 
(JNIEnv *env, jobject);
 
 #ifdef __cplusplus
 }
Index: include/gnu_java_awt_peer_gtk_GtkSelection.h
===================================================================
RCS file: include/gnu_java_awt_peer_gtk_GtkSelection.h
diff -N include/gnu_java_awt_peer_gtk_GtkSelection.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ include/gnu_java_awt_peer_gtk_GtkSelection.h        16 Aug 2005 17:29:16 
-0000
@@ -0,0 +1,23 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+
+#ifndef __gnu_java_awt_peer_gtk_GtkSelection__
+#define __gnu_java_awt_peer_gtk_GtkSelection__
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkSelection_requestText 
(JNIEnv *env, jobject);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkSelection_requestImage 
(JNIEnv *env, jobject);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkSelection_requestURIs 
(JNIEnv *env, jobject);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkSelection_requestBytes 
(JNIEnv *env, jobject, jstring);
+JNIEXPORT void JNICALL 
Java_gnu_java_awt_peer_gtk_GtkSelection_requestMimeTypes (JNIEnv *env, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __gnu_java_awt_peer_gtk_GtkSelection__ */
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c
===================================================================
RCS file: 
/cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c,v
retrieving revision 1.12
diff -u -r1.12 gnu_java_awt_peer_gtk_GtkClipboard.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c    14 Jul 2005 
22:07:02 -0000      1.12
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c    16 Aug 2005 
17:29:17 -0000
@@ -1,5 +1,5 @@
 /* gtkclipboard.c
-   Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2005 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -36,169 +36,739 @@
 exception statement from your version. */
 
 
+#include "jcl.h"
 #include "gtkpeer.h"
 #include "gnu_java_awt_peer_gtk_GtkClipboard.h"
+#include "gnu_java_awt_peer_gtk_GtkSelection.h"
 
-static jmethodID stringSelectionReceivedID;
-static jmethodID stringSelectionHandlerID;
-static jmethodID selectionClearID;
-
-static void selection_received_cb (GtkWidget *, GtkSelectionData *,
-                                   guint, gpointer);
-static void selection_get_cb (GtkWidget *, GtkSelectionData *, guint,
-                              guint, gpointer);
-static gint selection_clear_cb (GtkWidget *, GdkEventSelection *);
+#define OBJECT_TARGET 1
+#define TEXT_TARGET   2
+#define IMAGE_TARGET  3
+#define URI_TARGET    4
 
-static GtkWidget *clipboard;
-static jobject cb_obj;
+static GtkClipboard *clipboard;
 
-JNIEXPORT void JNICALL 
-Java_gnu_java_awt_peer_gtk_GtkClipboard_initNativeState (JNIEnv *env, 
-                                                        jobject obj)
+/* Simple id to keep track of the selection we are currently managing. */
+static int current_selection = 0;
+
+/* Whether we "own" the clipboard. And may clear it. */
+static int owner = 0;
+
+static jclass gtk_clipboard_class;
+static jmethodID setSystemContentsID;
+
+static jobject gtk_clipboard_instance = NULL;
+static jmethodID provideContentID;
+static jmethodID provideTextID;
+static jmethodID provideImageID;
+static jmethodID provideURIsID;
+
+static jstring stringTarget;
+static jstring imageTarget;
+static jstring filesTarget;
+
+static void
+cp_gtk_clipboard_owner_change (GtkClipboard *clipboard __attribute__((unused)),
+                              GdkEvent *event __attribute__((unused)),
+                              gpointer user_data __attribute__((unused)))
 {
+  /* These are only interesting when we are not the owner. Otherwise
+     we will have the set and clear functions doing the updating. */
+  JNIEnv *env = cp_gtk_gdk_env ();
+  if (!owner)
+    (*env)->CallStaticVoidMethod (env, gtk_clipboard_class,
+                                 setSystemContentsID);
+}
+
+JNIEXPORT jboolean JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkClipboard_initNativeState (JNIEnv *env,
+                                                        jclass gtkclipboard,
+                                                        jstring string,
+                                                        jstring image,
+                                                        jstring files)
+{
+  GdkDisplay* display;
+  jboolean can_cache;
+
+  gtk_clipboard_class = gtkclipboard;
+  setSystemContentsID = (*env)->GetStaticMethodID (env, gtk_clipboard_class,
+                                                  "setSystemContents",
+                                                  "()V");
+  if (setSystemContentsID == NULL)
+    return JNI_FALSE;
+
+  stringTarget = (*env)->NewGlobalRef(env, string);
+  imageTarget = (*env)->NewGlobalRef(env, image);
+  filesTarget = (*env)->NewGlobalRef(env, files);
+
   gdk_threads_enter ();
+  clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
-  if (!stringSelectionReceivedID)
+  display = gtk_clipboard_get_display (clipboard);
+  if (gdk_display_supports_selection_notification (display))
     {
-      jclass gtkclipboard;
-
-      gtkclipboard = (*env)->FindClass (env, 
-                                       "gnu/java/awt/peer/gtk/GtkClipboard");
-      stringSelectionReceivedID = (*env)->GetMethodID (env, gtkclipboard,
-                                                   "stringSelectionReceived",
-                                                   "(Ljava/lang/String;)V");
-      stringSelectionHandlerID = (*env)->GetMethodID (env, gtkclipboard,
-                                                     "stringSelectionHandler",
-                                                     "()Ljava/lang/String;");
-      selectionClearID = (*env)->GetMethodID (env, gtkclipboard,
-                                             "selectionClear", "()V");
+      g_signal_connect (clipboard, "owner-change",
+                       G_CALLBACK (cp_gtk_clipboard_owner_change), NULL);
+      gdk_display_request_selection_notification (display,
+                                                 GDK_SELECTION_CLIPBOARD);
+      can_cache = JNI_TRUE;
     }
+  else
+    can_cache = JNI_FALSE;
+  gdk_threads_leave ();
 
-  cb_obj = (*env)->NewGlobalRef (env, obj);
-
-  clipboard = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  return can_cache;
+}
 
-  g_signal_connect (G_OBJECT(clipboard), "selection_received",
-                    G_CALLBACK (selection_received_cb), NULL);
+static void
+cp_gtk_clipboard_get_func (GtkClipboard *clipboard __attribute__((unused)),
+                          GtkSelectionData *selection,
+                          guint info,
+                          gpointer user_data __attribute__((unused)))
+{
+  JNIEnv *env = cp_gtk_gdk_env ();
+  
+  if (info == OBJECT_TARGET)
+    {
+      const gchar *target_name;
+      jstring target_string;
+      jbyteArray bytes;
+      jint len;
+      jbyte *barray;
+
+      target_name = gdk_atom_name (selection->target);
+      if (target_name == NULL)
+       return;
+      target_string = (*env)->NewStringUTF (env, target_name);
+      if (target_string == NULL)
+       return;
+      bytes = (*env)->CallObjectMethod(env,
+                                      gtk_clipboard_instance,
+                                      provideContentID,
+                                      target_string);
+      if (bytes == NULL)
+       return;
+      len = (*env)->GetArrayLength(env, bytes);
+      if (len <= 0)
+       return;
+      barray = (*env)->GetByteArrayElements(env, bytes, NULL);
+      if (barray == NULL)
+       return;
+      gtk_selection_data_set (selection, selection->target, 8,
+                             (guchar *) barray, len);
 
-  g_signal_connect (G_OBJECT(clipboard), "selection_clear_event",
-                    G_CALLBACK (selection_clear_cb), NULL);
+      (*env)->ReleaseByteArrayElements(env, bytes, barray, 0);
 
-  gtk_selection_add_target (clipboard, GDK_SELECTION_PRIMARY, 
-                           GDK_TARGET_STRING, 0);
+    }
+  else if (info == TEXT_TARGET)
+    {
+      jstring string;
+      const gchar *text;
+      int len;
+      string = (*env)->CallObjectMethod(env,
+                                       gtk_clipboard_instance,
+                                       provideTextID);
+      if (string == NULL)
+       return;
+      len = (*env)->GetStringUTFLength (env, string);
+      if (len == -1)
+       return;
+      text = (*env)->GetStringUTFChars (env, string, NULL);
+      if (text == NULL)
+       return;
 
-  g_signal_connect (G_OBJECT(clipboard), "selection_get",
-                    G_CALLBACK (selection_get_cb), NULL);
+      gtk_selection_data_set_text (selection, text, len);
+      (*env)->ReleaseStringUTFChars (env, string, text);
+    }
+  else if (info == IMAGE_TARGET)
+    {
+      jobject gtkimage;
+      GdkPixbuf *pixbuf = NULL;
+      
+      gtkimage = (*env)->CallObjectMethod(env,
+                                         gtk_clipboard_instance,
+                                         provideImageID);
+      if (gtkimage == NULL)
+       return;
+      
+      pixbuf = cp_gtk_image_get_pixbuf (env, gtkimage);
+      if (pixbuf != NULL)
+       {
+         gtk_selection_data_set_pixbuf (selection, pixbuf);
+
+         /* if the GtkImage is offscreen, this is a temporary pixbuf
+            which should be thrown out. */
+         if(cp_gtk_image_is_offscreen (env, gtkimage) == JNI_TRUE)
+           gdk_pixbuf_unref (pixbuf);
+       }
+    }
+  else if (info == URI_TARGET)
+    {
+      jobjectArray uris;
+      jint count;
+      int i;
+      gchar **list;
+
+      uris = (*env)->CallObjectMethod(env,
+                                     gtk_clipboard_instance,
+                                     provideURIsID);
+      if (uris == NULL)
+       return;
+      count = (*env)->GetArrayLength (env, uris);
+      if (count <= 0)
+       return;
+
+      list = (gchar **) JCL_malloc (env, (count + 1) * sizeof (gchar *));
+      for (i = 0; i < count; i++)
+       {
+         const char *text;
+         jstring uri;
+         
+         /* Mark NULL in so case of some error we can find the end. */
+         list[i] = NULL;
+         uri = (*env)->GetObjectArrayElement (env, uris, i);
+         if (uri == NULL)
+           break;
+         text = (*env)->GetStringUTFChars (env, uri, NULL);
+         if (text == NULL)
+           break;
+         list[i] = strdup (text);
+         (*env)->ReleaseStringUTFChars (env, uri, text);
+       }
+
+      if (i == count)
+       {
+         list[count] = NULL;
+         gtk_selection_data_set_uris (selection, list);
+       }
+
+      for (i = 0; list[i] != NULL; i++)
+       free (list[i]);
+      JCL_free (env, list);
+    }
+}
 
-  gdk_threads_leave ();
+static void
+cp_gtk_clipboard_clear_func (GtkClipboard *clipboard __attribute__((unused)),
+                            gpointer user_data)
+{
+  if (owner && (int) user_data == current_selection)
+    {
+      JNIEnv *env = cp_gtk_gdk_env();
+      owner = 0;
+      (*env)->CallStaticVoidMethod (env, gtk_clipboard_class,
+                                   setSystemContentsID);
+    }
 }
 
 JNIEXPORT void JNICALL
-Java_gnu_java_awt_peer_gtk_GtkClipboard_requestStringConversion
-  (JNIEnv *env __attribute__((unused)), jclass clazz __attribute__((unused)))
+Java_gnu_java_awt_peer_gtk_GtkClipboard_advertiseContent
+(JNIEnv *env,
+ jobject instance,
+ jobjectArray mime_array,
+ jboolean add_text,
+ jboolean add_images __attribute__((unused)),
+ jboolean add_uris __attribute__((unused)))
 {
+  GtkTargetList *target_list;
+  GList *list;
+  GtkTargetEntry *targets;
+  gint n, i;
+
   gdk_threads_enter ();
+  target_list = gtk_target_list_new (NULL, 0);
+
+  if (mime_array != NULL)
+    {
+      n = (*env)->GetArrayLength (env, mime_array);
+      for (i = 0; i < n; i++)
+       {
+         const char *text;
+         jstring target;
+         GdkAtom atom;
+
+         target = (*env)->GetObjectArrayElement (env, mime_array, i);
+         if (target == NULL)
+           break;
+         text = (*env)->GetStringUTFChars (env, target, NULL);
+         if (text == NULL)
+           break;
 
-  gtk_selection_convert (clipboard, GDK_SELECTION_PRIMARY, 
-                        GDK_TARGET_STRING, GDK_CURRENT_TIME);
+         atom = gdk_atom_intern (text, FALSE);
+         gtk_target_list_add (target_list, atom, 0, OBJECT_TARGET);
 
+         (*env)->ReleaseStringUTFChars (env, target, text);
+       }
+    }
+
+  /* Add extra targets that gtk+ can provide/translate for us. */
+  if (add_text)
+    gtk_target_list_add_text_targets (target_list, TEXT_TARGET);
+  if (add_images)
+    gtk_target_list_add_image_targets (target_list, IMAGE_TARGET, TRUE);
+  if (add_uris)
+    gtk_target_list_add_uri_targets (target_list, URI_TARGET);
+
+  /* Turn list into a target table. */
+  n = g_list_length (target_list->list);
+  if (n > 0)
+    {
+      targets = g_new (GtkTargetEntry, n);
+      for (list = target_list->list, i = 0;
+          list != NULL;
+          list = list->next, i++)
+       {
+         GtkTargetPair *pair = (GtkTargetPair *) list->data;
+         targets[i].target = gdk_atom_name (pair->target);
+         targets[i].flags = pair->flags;
+         targets[i].info = pair->info;
+       }
+
+      /* Set the targets plus callback functions and ask for the clipboard
+        to be stored when the application exists. */
+      current_selection++;
+      if (gtk_clipboard_set_with_data (clipboard, targets, n,
+                                      cp_gtk_clipboard_get_func,
+                                      cp_gtk_clipboard_clear_func,
+                                      (gpointer) current_selection))
+       {
+         owner = 1;
+         if (gtk_clipboard_instance == NULL)
+           {
+             JNIEnv *env = cp_gtk_gdk_env ();
+             gtk_clipboard_instance =  (*env)->NewGlobalRef(env, instance);
+
+             provideContentID
+               = (*env)->GetMethodID (env, gtk_clipboard_class,
+                                      "provideContent",
+                                      "(Ljava/lang/String;)[B");
+             if (provideContentID == NULL)
+               return;
+
+             provideTextID
+               = (*env)->GetMethodID (env, gtk_clipboard_class,
+                                      "provideText", "()Ljava/lang/String;");
+             if (provideTextID == NULL)
+               return;
+
+             provideImageID
+               = (*env)->GetMethodID (env, gtk_clipboard_class,
+                                      "provideImage",
+                                      "()Lgnu/java/awt/peer/gtk/GtkImage;");
+             if (provideImageID == NULL)
+               return;
+
+             provideURIsID
+               = (*env)->GetMethodID (env, gtk_clipboard_class,
+                                      "provideURIs",
+                                      "()[Ljava/lang/String;");
+             if (provideURIsID == NULL)
+               return;
+           }
+         gtk_clipboard_set_can_store (clipboard, NULL, 0);
+       }
+      else
+       {
+         owner = 0;
+         (*env)->CallStaticVoidMethod (env, gtk_clipboard_class,
+                                       setSystemContentsID);
+       }
+
+      for (i = 0; i < n; i++)
+       g_free (targets[i].target);
+      g_free (targets);
+    }
+  else if (owner)
+    {
+      gtk_clipboard_clear (clipboard);
+      owner = 0;
+    }
+
+  gtk_target_list_unref (target_list);
   gdk_threads_leave ();
 }
 
+
+/* GtkSelection native methods. Put here for convenience since they
+   need access to the current clipboard. */
+
+static jmethodID mimeTypesAvailableID;
+
+/* Note this is actually just a GtkClipboardReceivedFunc, not a real
+   GtkClipboardTargetsReceivedFunc, see requestMimeTypes. */
 static void
-selection_received_cb (GtkWidget *widget __attribute__((unused)),
-                       GtkSelectionData *selection_data 
__attribute__((unused)),
-                       guint time __attribute__((unused)),
-                       gpointer data __attribute__((unused)))
+cp_gtk_clipboard_targets_received (GtkClipboard *clipboard
+                                  __attribute__((unused)),
+                                  GtkSelectionData *target_data,
+                                  gpointer selection)
 {
-  /* Check to see if retrieval succeeded  */
-  if (selection_data->length < 0
-      || selection_data->type != GDK_SELECTION_TYPE_STRING)
+  GdkAtom *targets = NULL;
+  gint targets_len = 0;
+  gchar **target_strings = NULL;
+  jobjectArray strings = NULL;
+  int strings_len = 0;
+  gboolean include_text = FALSE;
+  gboolean include_image = FALSE;
+  gboolean include_uris = FALSE;
+  jobject selection_obj = (jobject) selection;
+  JNIEnv *env = cp_gtk_gdk_env ();
+
+  if (target_data != NULL && target_data->length > 0)
     {
-      gdk_threads_leave ();
+      include_text = gtk_selection_data_targets_include_text (target_data);
+      include_image = gtk_selection_data_targets_include_image (target_data,
+                                                               TRUE);
+      if (gtk_selection_data_get_targets (target_data, &targets, &targets_len))
+       {
+         int i;
+         GdkAtom uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
+         target_strings = g_new (gchar*, targets_len);
+         if (target_strings != NULL)
+           for (i = 0; i < targets_len; i++)
+             {
+               gchar *name =  gdk_atom_name (targets[i]);
+               if (strchr (name, '/') != NULL)
+                 {
+                   target_strings[i] = name;
+                   strings_len++;
+                   if (! include_uris && targets[i] == uri_list_atom)
+                     include_uris = TRUE;
+                 }
+               else
+                 target_strings[i] = NULL;
+             }
+       }
+
+      if (target_strings != NULL)
+       {
+         int i = 0, j = 0;
+         jclass stringClass;
+         
+         if (include_text)
+           strings_len++;
+         if (include_image)
+           strings_len++;
+         if (include_uris)
+           strings_len++;
+         
+         stringClass = (*env)->FindClass (env, "java/lang/String");
+         strings = (*env)->NewObjectArray (env, strings_len, stringClass,
+                                           NULL);
+         if (strings != NULL)
+           {
+             if (include_text)
+               (*env)->SetObjectArrayElement (env, strings, i++,
+                                              stringTarget);
+             if (include_image)
+               (*env)->SetObjectArrayElement (env, strings, i++,
+                                              imageTarget);
+             if (include_uris)
+               (*env)->SetObjectArrayElement (env, strings, i++,
+                                              filesTarget);
+             
+             while(i < strings_len)
+               {
+                 if (target_strings[j] == NULL)
+                   j++;
+                 else
+                   {
+                     jstring string;
+                     string = (*env)->NewStringUTF (env,
+                                                    target_strings[j++]);
+                     if (string == NULL)
+                       break;
+                     (*env)->SetObjectArrayElement (env, strings, i++,
+                                                    string);
+                   }
+               }
+           }
+
+         for (i = 0; i < targets_len; i++)
+           g_free (target_strings[i]);
+         g_free (target_strings);
+       }
+    }
+
+  (*env)->CallVoidMethod (env, selection_obj,
+                         mimeTypesAvailableID,
+                         strings);
+  (*env)->DeleteGlobalRef (env, selection_obj);
+}
 
-      (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, 
stringSelectionReceivedID,
-                                   NULL);
+JNIEXPORT void JNICALL 
+Java_gnu_java_awt_peer_gtk_GtkSelection_requestMimeTypes
+(JNIEnv *env, jobject selection)
+{
+  jobject selection_obj;
+  selection_obj = (*env)->NewGlobalRef(env, selection);
+  if (selection_obj == NULL)
+    return;
 
-      gdk_threads_enter ();
+  if (mimeTypesAvailableID == NULL)
+    {
+      jclass gtk_selection_class;
+      gtk_selection_class = (*env)->GetObjectClass (env, selection_obj);
+      mimeTypesAvailableID = (*env)->GetMethodID (env, gtk_selection_class,
+                                               "mimeTypesAvailable",
+                                               "([Ljava/lang/String;)V");
+      if (mimeTypesAvailableID == NULL)
+       return;
     }
+
+  /* We would have liked to call gtk_clipboard_request_targets ()
+     since that is more general. But the result of that, an array of
+     GdkAtoms, cannot be used with the
+     gtk_selection_data_targets_include_<x> functions (despite what
+     the name suggests). */
+  gdk_threads_enter ();
+  gtk_clipboard_request_contents (clipboard,
+                                 gdk_atom_intern ("TARGETS", FALSE),
+                                 cp_gtk_clipboard_targets_received,
+                                 (gpointer) selection_obj);
+  gdk_threads_leave ();
+}
+
+
+static jmethodID textAvailableID;
+
+static void
+cp_gtk_clipboard_text_received (GtkClipboard *clipboard
+                               __attribute__((unused)),
+                               const gchar *text,
+                               gpointer selection)
+{
+  jstring string;
+  jobject selection_obj = (jobject) selection;
+
+  JNIEnv *env = cp_gtk_gdk_env ();
+  if (text != NULL)
+    string = (*env)->NewStringUTF (env, text);
   else
-    {
-      char *str = (char *) selection_data->data;
-      
-      gdk_threads_leave ();
+    string = NULL;
 
-      (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, 
stringSelectionReceivedID,
-                                   (*cp_gtk_gdk_env())->NewStringUTF 
(cp_gtk_gdk_env(), str));
+  (*env)->CallVoidMethod (env, selection_obj,
+                          textAvailableID,
+                          string);
+  (*env)->DeleteGlobalRef (env, selection_obj);
+}
 
-      gdk_threads_enter ();
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GtkSelection_requestText
+(JNIEnv *env, jobject selection)
+{
+  jobject selection_obj;
+  selection_obj = (*env)->NewGlobalRef(env, selection);
+  if (selection_obj == NULL)
+    return;
+
+  if (textAvailableID == NULL)
+    {
+      jclass gtk_selection_class;
+      gtk_selection_class = (*env)->GetObjectClass (env, selection_obj);
+      textAvailableID = (*env)->GetMethodID (env, gtk_selection_class,
+                                            "textAvailable",
+                                            "(Ljava/lang/String;)V");
+      if (textAvailableID == NULL)
+        return;
     }
 
-  return;
+  gdk_threads_enter ();
+  gtk_clipboard_request_text (clipboard,
+                             cp_gtk_clipboard_text_received,
+                             (gpointer) selection_obj);
+  gdk_threads_leave ();
 }
 
+static jmethodID imageAvailableID;
+
 static void
-selection_get_cb (GtkWidget *widget __attribute__((unused)), 
-                  GtkSelectionData *selection_data,
-                  guint      info __attribute__((unused)),
-                  guint      time __attribute__((unused)),
-                  gpointer   data __attribute__((unused)))
+cp_gtk_clipboard_image_received (GtkClipboard *clipboard
+                                __attribute__((unused)),
+                                GdkPixbuf *pixbuf,
+                                gpointer selection)
 {
-  jstring jstr;
-  const char *utf;
-  jsize utflen;
+  jobject pointer = NULL;
+  jobject selection_obj = (jobject) selection;
+  JNIEnv *env = cp_gtk_gdk_env ();
 
-  gdk_threads_leave ();
+  if (pixbuf != NULL)
+    {
+      g_object_ref (pixbuf);
+      pointer = JCL_NewRawDataObject (env, (void *) pixbuf);
+    }
 
-  jstr = (*cp_gtk_gdk_env())->CallObjectMethod (cp_gtk_gdk_env(), cb_obj, 
-                                      stringSelectionHandlerID);
+  (*env)->CallVoidMethod (env, selection_obj,
+                         imageAvailableID,
+                          pointer);
+  (*env)->DeleteGlobalRef (env, selection_obj);
+}
 
-  gdk_threads_enter ();
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GtkSelection_requestImage (JNIEnv *env, jobject obj)
+{
+  jobject selection_obj;
+  selection_obj = (*env)->NewGlobalRef(env, obj);
+  if (selection_obj == NULL)
+    return;
 
-  if (!jstr)
+  if (imageAvailableID == NULL)
     {
-      gtk_selection_data_set (selection_data, 
-                             GDK_TARGET_STRING, 8, NULL, 0);
-      return;
+      jclass gtk_selection_class;
+      gtk_selection_class = (*env)->GetObjectClass (env, selection_obj);
+      imageAvailableID = (*env)->GetMethodID (env, gtk_selection_class,
+                                            "imageAvailable",
+                                            "(Lgnu/classpath/Pointer;)V");
+      if (imageAvailableID == NULL)
+        return;
     }
 
-  utflen = (*cp_gtk_gdk_env())->GetStringUTFLength (cp_gtk_gdk_env(), jstr);
-  utf = (*cp_gtk_gdk_env())->GetStringUTFChars (cp_gtk_gdk_env(), jstr, NULL);
+  gdk_threads_enter ();
+  gtk_clipboard_request_image (clipboard,
+                              cp_gtk_clipboard_image_received,
+                              (gpointer) selection_obj);
+  gdk_threads_leave ();
+}
 
-  gtk_selection_data_set (selection_data, GDK_TARGET_STRING, 8,
-                         (const unsigned char*)utf, utflen);
+static jmethodID urisAvailableID;
+
+static void
+cp_gtk_clipboard_uris_received (GtkClipboard *clipboard
+                               __attribute__((unused)),
+                               GtkSelectionData *uri_data,
+                               gpointer selection)
+{
+  gchar **uris = NULL;
+  jobjectArray strings = NULL;
+  jobject selection_obj = (jobject) selection;
+  JNIEnv *env = cp_gtk_gdk_env ();
+
+  if (uri_data != NULL)
+    uris = gtk_selection_data_get_uris (uri_data);
+  
+  if (uris != NULL)
+    {
+      int len, i;
+      gchar **count = uris;
+      jclass stringClass = (*env)->FindClass (env, "java/lang/String");
+
+      len = 0;
+      while (count[len])
+       len++;
+
+      strings = (*env)->NewObjectArray (env, len, stringClass, NULL);
+      if (strings != NULL)
+       {
+         for (i = 0; i < len; i++)
+           {
+             jstring string = (*env)->NewStringUTF (env, uris[i]);
+             if (string == NULL)
+               break;
+             (*env)->SetObjectArrayElement (env, strings, i, string);
+           }
+       }
+      g_strfreev (uris);
+    }
 
-  (*cp_gtk_gdk_env())->ReleaseStringUTFChars (cp_gtk_gdk_env(), jstr, utf);
+  (*env)->CallVoidMethod (env, selection_obj,
+                          urisAvailableID,
+                          strings);
+  (*env)->DeleteGlobalRef (env, selection_obj);
 }
 
 JNIEXPORT void JNICALL
-Java_gnu_java_awt_peer_gtk_GtkClipboard_selectionGet
-  (JNIEnv *env, jclass clazz __attribute__((unused)))
+Java_gnu_java_awt_peer_gtk_GtkSelection_requestURIs (JNIEnv *env, jobject obj)
 {
-  GdkWindow *owner;
+  GdkAtom uri_atom;
+  jobject selection_obj;
+  selection_obj = (*env)->NewGlobalRef(env, obj);
+  if (selection_obj == NULL)
+    return;
 
+  if (urisAvailableID == NULL)
+    {
+      jclass gtk_selection_class;
+      gtk_selection_class = (*env)->GetObjectClass (env, selection_obj);
+      urisAvailableID = (*env)->GetMethodID (env, gtk_selection_class,
+                                            "urisAvailable",
+                                             "([Ljava/lang/String;)V");
+      if (urisAvailableID == NULL)
+        return;
+    }
+
+  /* There is no real request_uris so we have to make one ourselves. */
   gdk_threads_enter ();
+  uri_atom = gdk_atom_intern ("text/uri-list", FALSE);
+  gtk_clipboard_request_contents (clipboard,
+                                 uri_atom,
+                                 cp_gtk_clipboard_uris_received,
+                                 (gpointer) selection_obj);
+  gdk_threads_leave ();
+}
 
-  /* if we already own the clipboard, we need to tell the old data object
-     that we're no longer going to be using him */
-  owner = gdk_selection_owner_get (GDK_SELECTION_PRIMARY);
-  if (owner && owner == clipboard->window)
-    (*env)->CallVoidMethod (env, cb_obj, selectionClearID);
-    
-  gtk_selection_owner_set (clipboard, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
+static jmethodID bytesAvailableID;
 
-  gdk_threads_leave ();
+static void
+cp_gtk_clipboard_bytes_received (GtkClipboard *clipboard
+                                __attribute__((unused)),
+                                GtkSelectionData *selection_data,
+                                gpointer selection)
+{
+  jbyteArray bytes = NULL;
+  jobject selection_obj = (jobject) selection;
+  JNIEnv *env = cp_gtk_gdk_env ();
+
+   if (selection_data != NULL && selection_data->length > 0)
+    {
+      bytes = (*env)->NewByteArray (env, selection_data->length);
+      if (bytes != NULL)
+       (*env)->SetByteArrayRegion(env, bytes, 0, selection_data->length,
+                                  (jbyte *) selection_data->data);
+    }
+
+  (*env)->CallVoidMethod (env, selection_obj,
+                          bytesAvailableID,
+                          bytes);
+  (*env)->DeleteGlobalRef (env, selection_obj);
 }
 
-static gint
-selection_clear_cb (GtkWidget *widget __attribute__((unused)),
-                    GdkEventSelection *event __attribute__((unused)))
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GtkSelection_requestBytes (JNIEnv *env,
+                                                     jobject obj,
+                                                     jstring target_string)
 {
-  gdk_threads_leave ();
+  int len;
+  const gchar *target_text;
+  GdkAtom target_atom;
+  jobject selection_obj;
+  selection_obj = (*env)->NewGlobalRef(env, obj);
+  if (selection_obj == NULL)
+    return;
+
+  if (bytesAvailableID == NULL)
+    {
+      jclass gtk_selection_class;
+      gtk_selection_class = (*env)->GetObjectClass (env, selection_obj);
+      bytesAvailableID = (*env)->GetMethodID (env, gtk_selection_class,
+                                             "bytesAvailable",
+                                             "([B)V");
+      if (bytesAvailableID == NULL)
+        return;
+    }
 
-  (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, 
selectionClearID);
+  len = (*env)->GetStringUTFLength (env, target_string);
+  if (len == -1)
+    return;
+  target_text = (*env)->GetStringUTFChars (env, target_string, NULL);
+  if (target_text == NULL)
+    return;
 
   gdk_threads_enter ();
+  target_atom = gdk_atom_intern (target_text, FALSE);
+  gtk_clipboard_request_contents (clipboard,
+                                  target_atom,
+                                  cp_gtk_clipboard_bytes_received,
+                                  (gpointer) selection_obj);
+  gdk_threads_leave ();
 
-  return TRUE;
+  (*env)->ReleaseStringUTFChars (env, target_string, target_text);
 }
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c
===================================================================
RCS file: 
/cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c,v
retrieving revision 1.11
diff -u -r1.11 gnu_java_awt_peer_gtk_GtkImage.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c        5 Aug 2005 
17:12:52 -0000       1.11
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c        16 Aug 2005 
17:29:17 -0000
@@ -97,6 +97,19 @@
   return JNI_TRUE;
 }
 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GtkImage_createFromPixbuf
+(JNIEnv *env, jobject obj)
+{
+  int width, heigth;
+  GdkPixbuf *pixbuf = (GdkPixbuf *) getData (env, obj);
+  gdk_threads_enter ();
+  width =  gdk_pixbuf_get_width (pixbuf);
+  heigth = gdk_pixbuf_get_height (pixbuf);
+  gdk_threads_leave ();
+  setWidthHeight(env, obj, width, heigth);
+}
+
 /**
  * Returns a copy of the pixel data as a java array.
  */

Attachment: signature.asc
Description: This is a digitally signed message part


reply via email to

[Prev in Thread] Current Thread [Next in Thread]