OSDN Git Service

Imported Classpath 0.18.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / gtk-peer / gnu_java_awt_peer_gtk_GtkClipboard.c
index dea9d4c..f10a80d 100644 (file)
@@ -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,377 @@ 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"
 
-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 *);
+/* The clipboard and standard (string targets) shared with GtkSelection. */
+GtkClipboard *cp_gtk_clipboard;
 
-static GtkWidget *clipboard;
-static jobject cb_obj;
+jstring cp_gtk_stringTarget;
+jstring cp_gtk_imageTarget;
+jstring cp_gtk_filesTarget;
 
-JNIEXPORT void JNICALL 
-Java_gnu_java_awt_peer_gtk_GtkClipboard_initNativeState (JNIEnv *env, 
-                                                        jobject obj)
-{
-  gdk_threads_enter ();
+/* Simple id to keep track of the selection we are currently managing. */
+static int current_selection = 0;
 
-  if (!stringSelectionReceivedID)
-    {
-      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");
-    }
+/* Whether we "own" the clipboard. And may clear it. */
+static int owner = 0;
 
-  cb_obj = (*env)->NewGlobalRef (env, obj);
+static jclass gtk_clipboard_class;
+static jmethodID setSystemContentsID;
 
-  clipboard = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+static jobject gtk_clipboard_instance = NULL;
+static jmethodID provideContentID;
+static jmethodID provideTextID;
+static jmethodID provideImageID;
+static jmethodID provideURIsID;
 
-  g_signal_connect (G_OBJECT(clipboard), "selection_received",
-                    G_CALLBACK (selection_received_cb), NULL);
-
-  g_signal_connect (G_OBJECT(clipboard), "selection_clear_event",
-                    G_CALLBACK (selection_clear_cb), NULL);
-
-  gtk_selection_add_target (clipboard, GDK_SELECTION_PRIMARY, 
-                           GDK_TARGET_STRING, 0);
+/* Called when clipboard owner changes. Used to update available targets. */
+#if GTK_MINOR_VERSION > 4
+static void
+clipboard_owner_change_cb (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);
+}
+#endif
+
+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_get",
-                    G_CALLBACK (selection_get_cb), NULL);
+  gtk_clipboard_class = gtkclipboard;
+  setSystemContentsID = (*env)->GetStaticMethodID (env, gtk_clipboard_class,
+                                                  "setSystemContents",
+                                                  "()V");
+  if (setSystemContentsID == NULL)
+    return JNI_FALSE;
 
-  gdk_threads_leave ();
-}
+  cp_gtk_stringTarget = (*env)->NewGlobalRef(env, string);
+  cp_gtk_imageTarget = (*env)->NewGlobalRef(env, image);
+  cp_gtk_filesTarget = (*env)->NewGlobalRef(env, files);
 
-JNIEXPORT void JNICALL
-Java_gnu_java_awt_peer_gtk_GtkClipboard_requestStringConversion
-  (JNIEnv *env __attribute__((unused)), jclass clazz __attribute__((unused)))
-{
   gdk_threads_enter ();
+  cp_gtk_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
-  gtk_selection_convert (clipboard, GDK_SELECTION_PRIMARY, 
-                        GDK_TARGET_STRING, GDK_CURRENT_TIME);
-
+  display = gtk_clipboard_get_display (cp_gtk_clipboard);
+  /* Check for support for clipboard owner changes. */
+#if GTK_MINOR_VERSION > 4
+  if (gdk_display_supports_selection_notification (display))
+    {
+      g_signal_connect (cp_gtk_clipboard, "owner-change",
+                       G_CALLBACK (clipboard_owner_change_cb), NULL);
+      gdk_display_request_selection_notification (display,
+                                                 GDK_SELECTION_CLIPBOARD);
+      can_cache = JNI_TRUE;
+    }
+  else
+#endif
+    can_cache = JNI_FALSE;
   gdk_threads_leave ();
+
+  return can_cache;
 }
 
 static void
-selection_received_cb (GtkWidget *widget __attribute__((unused)),
-                       GtkSelectionData *selection_data __attribute__((unused)),
-                       guint time __attribute__((unused)),
-                       gpointer data __attribute__((unused)))
+clipboard_get_func (GtkClipboard *clipboard __attribute__((unused)),
+                   GtkSelectionData *selection,
+                   guint info,
+                   gpointer user_data __attribute__((unused)))
 {
-  /* Check to see if retrieval succeeded  */
-  if (selection_data->length < 0
-      || selection_data->type != GDK_SELECTION_TYPE_STRING)
+  JNIEnv *env = cp_gtk_gdk_env ();
+  
+  if (info == OBJECT_TARGET)
     {
-      gdk_threads_leave ();
-
-      (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, stringSelectionReceivedID,
-                                   NULL);
+      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);
 
-      gdk_threads_enter ();
     }
-  else
+  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);
+    }
+  /* Images and URIs/Files support only available with gtk+2.6 or higher. */
+#if GTK_MINOR_VERSION > 4
+  else if (info == IMAGE_TARGET)
     {
-      char *str = (char *) selection_data->data;
+      jobject gtkimage;
+      GdkPixbuf *pixbuf = NULL;
       
-      gdk_threads_leave ();
-
-      (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, stringSelectionReceivedID,
-                                   (*cp_gtk_gdk_env())->NewStringUTF (cp_gtk_gdk_env(), str));
-
-      gdk_threads_enter ();
+      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);
+       }
     }
-
-  return;
+  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);
+    }
+#endif
 }
 
 static void
-selection_get_cb (GtkWidget *widget __attribute__((unused)), 
-                  GtkSelectionData *selection_data,
-                  guint      info __attribute__((unused)),
-                  guint      time __attribute__((unused)),
-                  gpointer   data __attribute__((unused)))
+clipboard_clear_func (GtkClipboard *clipboard __attribute__((unused)),
+                     gpointer user_data)
 {
-  jstring jstr;
-  const char *utf;
-  jsize utflen;
-
-  gdk_threads_leave ();
-
-  jstr = (*cp_gtk_gdk_env())->CallObjectMethod (cp_gtk_gdk_env(), cb_obj, 
-                                      stringSelectionHandlerID);
-
-  gdk_threads_enter ();
-
-  if (!jstr)
+  if (owner && (int) user_data == current_selection)
     {
-      gtk_selection_data_set (selection_data, 
-                             GDK_TARGET_STRING, 8, NULL, 0);
-      return;
+      JNIEnv *env = cp_gtk_gdk_env();
+      owner = 0;
+      (*env)->CallStaticVoidMethod (env, gtk_clipboard_class,
+                                   setSystemContentsID);
     }
-
-  utflen = (*cp_gtk_gdk_env())->GetStringUTFLength (cp_gtk_gdk_env(), jstr);
-  utf = (*cp_gtk_gdk_env())->GetStringUTFChars (cp_gtk_gdk_env(), jstr, NULL);
-
-  gtk_selection_data_set (selection_data, GDK_TARGET_STRING, 8,
-                         (const unsigned char*)utf, utflen);
-
-  (*cp_gtk_gdk_env())->ReleaseStringUTFChars (cp_gtk_gdk_env(), jstr, utf);
 }
 
 JNIEXPORT void JNICALL
-Java_gnu_java_awt_peer_gtk_GtkClipboard_selectionGet
-  (JNIEnv *env, jclass clazz __attribute__((unused)))
+Java_gnu_java_awt_peer_gtk_GtkClipboard_advertiseContent
+(JNIEnv *env,
+ jobject instance,
+ jobjectArray mime_array,
+#if GTK_MINOR_VERSION > 4
+ jboolean add_text, jboolean add_images, jboolean add_uris)
+#else
+ jboolean add_text __attribute__((unused)),
+ jboolean add_images __attribute__((unused)),
+ jboolean add_uris __attribute__((unused)))
+#endif
 {
-  GdkWindow *owner;
+  GtkTargetList *target_list;
+  GList *list;
+  GtkTargetEntry *targets;
+  gint n, i;
 
   gdk_threads_enter ();
+  target_list = gtk_target_list_new (NULL, 0);
 
-  /* 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);
+  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);
+       }
+    }
 
-  gdk_threads_leave ();
-}
+  /* Add extra targets that gtk+ can provide/translate for us. */
+#if GTK_MINOR_VERSION > 4
+  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);
+#else
+  if (add_text)
+    gtk_target_list_add (target_list,
+                        gdk_atom_intern ("STRING", FALSE),
+                        0, TEXT_TARGET);
+#endif
+
+
+  /* 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 if supported. */
+      current_selection++;
+      if (gtk_clipboard_set_with_data (cp_gtk_clipboard, targets, n,
+                                      clipboard_get_func,
+                                      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;
+           }
+#if GTK_MINOR_VERSION > 4
+         gtk_clipboard_set_can_store (cp_gtk_clipboard, NULL, 0);
+#endif
+       }
+      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 (cp_gtk_clipboard);
+      owner = 0;
+    }
 
-static gint
-selection_clear_cb (GtkWidget *widget __attribute__((unused)),
-                    GdkEventSelection *event __attribute__((unused)))
-{
+  gtk_target_list_unref (target_list);
   gdk_threads_leave ();
-
-  (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), cb_obj, selectionClearID);
-
-  gdk_threads_enter ();
-
-  return TRUE;
 }