diff options
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | gnu/java/awt/peer/gtk/GtkClipboard.java | 390 | ||||
-rw-r--r-- | gnu/java/awt/peer/gtk/GtkClipboardNotifier.java | 112 | ||||
-rw-r--r-- | gnu/java/awt/peer/gtk/GtkImage.java | 19 | ||||
-rw-r--r-- | gnu/java/awt/peer/gtk/GtkSelection.java | 664 | ||||
-rw-r--r-- | gnu/java/awt/peer/gtk/GtkToolkit.java | 8 | ||||
-rw-r--r-- | include/Makefile.am | 1 | ||||
-rw-r--r-- | include/gnu_java_awt_peer_gtk_GtkClipboard.h | 7 | ||||
-rw-r--r-- | include/gnu_java_awt_peer_gtk_GtkImage.h | 1 | ||||
-rw-r--r-- | include/gnu_java_awt_peer_gtk_GtkSelection.h | 23 | ||||
-rw-r--r-- | native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c | 772 | ||||
-rw-r--r-- | native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c | 13 |
12 files changed, 1838 insertions, 192 deletions
@@ -1,3 +1,23 @@ +2005-08-16 Mark Wielaard <mark@klomp.org> + + * 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 + 2005-08-16 Anthony Balkissoon <abalkiss@redhat.com> * javax/swing/plaf/basic/BasicTableUI.java: diff --git a/gnu/java/awt/peer/gtk/GtkClipboard.java b/gnu/java/awt/peer/gtk/GtkClipboard.java index c719cddb5..a502e1fd6 100644 --- a/gnu/java/awt/peer/gtk/GtkClipboard.java +++ b/gnu/java/awt/peer/gtk/GtkClipboard.java @@ -38,133 +38,357 @@ exception statement from your version. */ 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 (); + // 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); + } - static boolean hasSelection = false; + /** + * The one and only gtk+ clipboard instance. + */ + private static GtkClipboard instance = new GtkClipboard(); - protected GtkClipboard() + /** + * Creates the clipboard and sets the initial contents to the + * current gtk+ selection. + */ + private GtkClipboard() { super("System Clipboard"); - initNativeState(); + setContents(new GtkSelection(), null); + } + + /** + * Returns the one and only GtkClipboard instance. + */ + + static GtkClipboard getInstance() + { + return instance; } - public Transferable getContents(Object requestor) + /** + * 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() { - synchronized (this) + 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 - { - selectionLock.wait(SELECTION_RECEIVED_TIMEOUT); - } - catch (InterruptedException e) + 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)) { - 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) + StringBuffer sb = new StringBuffer(); + char[] cs = new char[1024]; + int l = r.read(cs); + while (l != -1) { - numChars = reader.read(readBuf); - if (numChars == -1) - break; - sbuf.append(readBuf, 0, numChars); + 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) { } - - return selection; + catch (UnsupportedFlavorException ufe) + { + } + 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() + { + Transferable contents = this.contents; + if (contents == null || contents instanceof GtkSelection) + return null; + + try + { + return (GtkImage) contents.getTransferData(DataFlavor.imageFlavor); + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + catch (ClassCastException cce) + { + } + + return null; + } + + /** + * 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() { - selectionGet(); + Transferable contents = this.contents; + if (contents == null || contents instanceof GtkSelection) + return null; - this.contents = contents; - this.owner = owner; + try + { + 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) + { + } - hasSelection = true; + return null; } - synchronized void selectionClear() + /** + * 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) { - hasSelection = false; + // 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 (owner != null) + 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) { - owner.lostOwnership(this, contents); - owner = null; - contents = null; } + 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); } diff --git a/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java b/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java new file mode 100644 index 000000000..a470fe171 --- /dev/null +++ b/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java @@ -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(); + } + } + } +} diff --git a/gnu/java/awt/peer/gtk/GtkImage.java b/gnu/java/awt/peer/gtk/GtkImage.java index ad5988ea2..b1e72bb8c 100644 --- a/gnu/java/awt/peer/gtk/GtkImage.java +++ b/gnu/java/awt/peer/gtk/GtkImage.java @@ -241,6 +241,25 @@ public class GtkImage extends Image } /** + * 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, diff --git a/gnu/java/awt/peer/gtk/GtkSelection.java b/gnu/java/awt/peer/gtk/GtkSelection.java new file mode 100644 index 000000000..cb5db04e9 --- /dev/null +++ b/gnu/java/awt/peer/gtk/GtkSelection.java @@ -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(); +} diff --git a/gnu/java/awt/peer/gtk/GtkToolkit.java b/gnu/java/awt/peer/gtk/GtkToolkit.java index 46ff329c4..ef97685a3 100644 --- a/gnu/java/awt/peer/gtk/GtkToolkit.java +++ b/gnu/java/awt/peer/gtk/GtkToolkit.java @@ -88,7 +88,6 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit { Hashtable containers = new Hashtable(); static EventQueue q; - static Clipboard systemClipboard; static boolean useGraphics2dSet; static boolean useGraphics2d; @@ -136,7 +135,6 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit public GtkToolkit () { - systemClipboard = new GtkClipboard (); } public native void beep(); @@ -406,7 +404,11 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit public Clipboard getSystemClipboard() { - return systemClipboard; + SecurityManager secman = System.getSecurityManager(); + if (secman != null) + secman.checkSystemClipboardAccess(); + + return GtkClipboard.getInstance(); } /** diff --git a/include/Makefile.am b/include/Makefile.am index 7b698e63b..4a920dff6 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -57,6 +57,7 @@ $(top_srcdir)/include/gnu_java_awt_peer_gtk_GtkPanelPeer.h \ $(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 \ diff --git a/include/gnu_java_awt_peer_gtk_GtkClipboard.h b/include/gnu_java_awt_peer_gtk_GtkClipboard.h index 709a6c00d..6c76ed8dd 100644 --- a/include/gnu_java_awt_peer_gtk_GtkClipboard.h +++ b/include/gnu_java_awt_peer_gtk_GtkClipboard.h @@ -10,11 +10,8 @@ extern "C" { #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 } diff --git a/include/gnu_java_awt_peer_gtk_GtkImage.h b/include/gnu_java_awt_peer_gtk_GtkImage.h index 7aa1827c8..8ac0a75c3 100644 --- a/include/gnu_java_awt_peer_gtk_GtkImage.h +++ b/include/gnu_java_awt_peer_gtk_GtkImage.h @@ -18,6 +18,7 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkImage_freePixmap (JNIEnv *e 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 } diff --git a/include/gnu_java_awt_peer_gtk_GtkSelection.h b/include/gnu_java_awt_peer_gtk_GtkSelection.h new file mode 100644 index 000000000..576e0368d --- /dev/null +++ b/include/gnu_java_awt_peer_gtk_GtkSelection.h @@ -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__ */ diff --git a/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c b/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c index dea9d4cc7..d4d34227e 100644 --- a/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c +++ b/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c @@ -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 @@ obligated to do so. If you do not wish to do so, delete this 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; +#define OBJECT_TARGET 1 +#define TEXT_TARGET 2 +#define IMAGE_TARGET 3 +#define URI_TARGET 4 -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 *); +static GtkClipboard *clipboard; -static GtkWidget *clipboard; -static jobject cb_obj; +/* Simple id to keep track of the selection we are currently managing. */ +static int current_selection = 0; -JNIEXPORT void JNICALL -Java_gnu_java_awt_peer_gtk_GtkClipboard_initNativeState (JNIEnv *env, - jobject obj) -{ - gdk_threads_enter (); +/* Whether we "own" the clipboard. And may clear it. */ +static int owner = 0; - if (!stringSelectionReceivedID) - { - jclass gtkclipboard; +static jclass gtk_clipboard_class; +static jmethodID setSystemContentsID; - 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"); - } +static jobject gtk_clipboard_instance = NULL; +static jmethodID provideContentID; +static jmethodID provideTextID; +static jmethodID provideImageID; +static jmethodID provideURIsID; - cb_obj = (*env)->NewGlobalRef (env, obj); +static jstring stringTarget; +static jstring imageTarget; +static jstring filesTarget; - clipboard = gtk_window_new (GTK_WINDOW_TOPLEVEL); +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); +} - g_signal_connect (G_OBJECT(clipboard), "selection_received", - G_CALLBACK (selection_received_cb), NULL); +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; - g_signal_connect (G_OBJECT(clipboard), "selection_clear_event", - G_CALLBACK (selection_clear_cb), NULL); + gtk_clipboard_class = gtkclipboard; + setSystemContentsID = (*env)->GetStaticMethodID (env, gtk_clipboard_class, + "setSystemContents", + "()V"); + if (setSystemContentsID == NULL) + return JNI_FALSE; - gtk_selection_add_target (clipboard, GDK_SELECTION_PRIMARY, - GDK_TARGET_STRING, 0); + stringTarget = (*env)->NewGlobalRef(env, string); + imageTarget = (*env)->NewGlobalRef(env, image); + filesTarget = (*env)->NewGlobalRef(env, files); - g_signal_connect (G_OBJECT(clipboard), "selection_get", - G_CALLBACK (selection_get_cb), NULL); + gdk_threads_enter (); + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + display = gtk_clipboard_get_display (clipboard); + if (gdk_display_supports_selection_notification (display)) + { + 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 (); + + return can_cache; +} + +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); + + (*env)->ReleaseByteArrayElements(env, bytes, barray, 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; + + 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); + } +} + +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; + + atom = gdk_atom_intern (text, FALSE); + gtk_target_list_add (target_list, atom, 0, OBJECT_TARGET); + + (*env)->ReleaseStringUTFChars (env, target, text); + } + } - gtk_selection_convert (clipboard, GDK_SELECTION_PRIMARY, - GDK_TARGET_STRING, GDK_CURRENT_TIME); + /* 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; + + (*env)->CallVoidMethod (env, selection_obj, + textAvailableID, + string); + (*env)->DeleteGlobalRef (env, selection_obj); +} - (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, stringSelectionReceivedID, - (*cp_gtk_gdk_env())->NewStringUTF (cp_gtk_gdk_env(), str)); +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; - gdk_threads_enter (); + 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; - (*cp_gtk_gdk_env())->ReleaseStringUTFChars (cp_gtk_gdk_env(), jstr, utf); +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); + } + + (*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); } diff --git a/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c b/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c index b64862c65..772725396 100644 --- a/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c +++ b/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c @@ -97,6 +97,19 @@ Java_gnu_java_awt_peer_gtk_GtkImage_loadPixbuf 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. */ |