Hi,

I fixed the issues Tom stated below.

On Fri, 2006-06-30 at 13:03 -0400, Thomas Fitzsimmons wrote:
> Hi,
> 
> Lillian Angel wrote:
> > This patch fixes the long standing problem of not being able to
> > dynamically switch between a checkbox and a radio button. I.e. moving a
> > checkbox to a checkbox group changes the checkbox to a radio button.
> > 
> > Tom helped a lot with this patch. He removed the CheckboxGroupPeer and
> > we fixed it so everything is handled in GtkCheckboxPeer.
> > 
> > There is a mauve test for this. The harmony test
> > (test.java.awt.CheckboxTest) now passes.

[...]
> > +  if (groupPointer != 0)
> > +  {
> > +    native_group = JLONG_TO_PTR (GSList, groupPointer);
> > +    if(! GTK_IS_RADIO_BUTTON (native_group->data))
> > +      native_group = NULL;
> 
> When does it happen that the data field is not a GtkRadioButton?  I suspect 
> only 
> after removing a Checkbox from a CheckboxGroup?  This is likely a memory leak 
> -- 
> if the native_group's data field is no longer valid then we should probably 
> free 
> the GSList.

Gtk actually frees the list when needed. I changed these checks to
asserts and set the native_group to null in *_removeFromGroup (when
needed).

[...]

> > +  gdk_threads_leave ();
> > +  
> > +  return PTR_TO_JLONG (native_group);
> 
> I realized that there is a race condition here.  Instead of returning the 
> pointer value, we should use JNI to set the groupPointer field with the GDK 
> lock 
> held.

I changed all functions in GtkCheckboxPeer to be syncronized, and added
a new function to put the new group in the groupMap. This function is
called from the native peer.

[...]
> > +  NSA_DEL_PTR (env, obj);
> > +  NSA_SET_PTR (env, obj, container);
> 
> The NSA table points to the containing event box, so I don't think these two 
> lines are necessary.

Removed
> 
2006-06-30  Lillian Angel  <[EMAIL PROTECTED]>

        * gnu/java/awt/peer/gtk/GtkCheckboxPeer.java:
        Changed all return values of native functions to void.
        (create): Changed function to be syncronized. Removed
        call to put value in groupMap, this is now done from
        the native code.
        (setState): Changed function to be syncronized.
        (setLabel): Changed function to be syncronized.
        (setCheckboxGroup): Changed function to be syncronized. Removed
        call to put value in groupMap, this is now done from
        the native code.
        (postItemEvent): Changed function to be syncronized.
        (addToGroupMap): New function. Called by native code to add
        new value to the group.
        (dispose): Changed function to be syncronized.
        * include/gnu_java_awt_peer_gtk_GtkCheckboxPeer.h: Updated
        all functions.
        * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkCheckboxPeer.c
        (cp_gtk_checkbox_init_jni): Added code to link to
        java function.
        (Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_createRadioButton):
        Changed return value to void. Added call
        to java function to set pointer in groupMap.
        (Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_addtoGroup): 
        Likewise. Also, changed check to an assert. Also, removed call 
        to set/del pointer.
        (Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_removeFromGroup):
        Likewise. Also, added check to determine if native_group should 
        be set to NULL.
        (Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_switchToGroup): 
        Likewise.

Committed,
Lillian
Index: gnu/java/awt/peer/gtk/GtkCheckboxPeer.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkCheckboxPeer.java,v
retrieving revision 1.26
diff -u -r1.26 GtkCheckboxPeer.java
--- gnu/java/awt/peer/gtk/GtkCheckboxPeer.java	30 Jun 2006 14:47:17 -0000	1.26
+++ gnu/java/awt/peer/gtk/GtkCheckboxPeer.java	30 Jun 2006 19:19:55 -0000
@@ -60,11 +60,11 @@
   private static WeakHashMap groupMap = new WeakHashMap();
 
   public native void createCheckButton ();
-  public native long createRadioButton (long groupPointer);
+  public native void createRadioButton (long groupPointer);
 
-  public native long addToGroup (long groupPointer);
-  public native long removeFromGroup ();
-  public native long switchToGroup (long groupPointer);
+  public native void addToGroup (long groupPointer);
+  public native void removeFromGroup ();
+  public native void switchToGroup (long groupPointer);
   
   public native void connectSignals ();
 
@@ -80,7 +80,7 @@
     super (c);
   }
 
-  public void create ()
+  public synchronized void create ()
   {
     Checkbox checkbox = (Checkbox) awtComponent;
     current_group = checkbox.getCheckboxGroup ();
@@ -100,15 +100,14 @@
           {
             // We don't know about this group.  Create a new native
             // group pointer for this group and store it in our map.
-            groupMap.put(current_group, new Long (createRadioButton(0)));
+           createRadioButton(0);
           }
         else
           {
             // We already know about this group.  Pass the
             // corresponding native group pointer value to the native
             // create method.
-            groupMap.put(current_group,
-                         new Long(createRadioButton(groupPointer.longValue())));
+            createRadioButton(groupPointer.longValue());
           }
       }
     currentState = checkbox.getState();
@@ -125,7 +124,7 @@
    * event since events should only be posted for user initiated
    * clicks on the GtkCheckButton.
    */
-  synchronized public void setState (boolean state)
+  public synchronized void setState (boolean state)
   {
     if (currentState != state)
       {
@@ -134,12 +133,12 @@
       }
   }
 
-  public void setLabel (String label)
+  public synchronized void setLabel (String label)
   {
     gtkButtonSetLabel (label);
   }
 
-  public void setCheckboxGroup (CheckboxGroup group)
+  public synchronized void setCheckboxGroup (CheckboxGroup group)
   {
     if (current_group == null && group != null)
       {
@@ -156,15 +155,14 @@
           {
             // We don't know about this group.  Create a new native
             // group pointer for this group and store it in our map.
-            groupMap.put(current_group, new Long (addToGroup(0)));
+            addToGroup(0);
           }
         else
           {
             // We already know about this group.  Pass the
             // corresponding native group pointer value to the native
             // create method.
-            groupMap.put(current_group,
-                         new Long(addToGroup(groupPointer.longValue())));
+            addToGroup(groupPointer.longValue());
           }
       }
     else if (current_group != null && group == null)
@@ -173,8 +171,8 @@
         // removing it from a group.  This means that the backing
         // GtkWidget will change from a GtkRadioButton to a
         // GtkCheckButton.
-        groupMap.put(current_group, new Long (removeFromGroup()));
-        current_group = group;
+        removeFromGroup();
+        current_group = null;
       }
     else if (current_group == null && group == null)
       {
@@ -189,30 +187,30 @@
         // remove the backing GtkRadioButton from one group and add it
         // to the other group.
 
+        current_group = group;
+        
         // See if the new group is already stored in our map.
         Long groupPointer = (Long) groupMap.get(group);
         if (groupPointer == null)
           {
             // We don't know about this group.  Create a new native
             // group pointer for this group and store it in our map.
-            groupMap.put(group, new Long (switchToGroup(0)));
+            switchToGroup(0);
           }
         else
           {
             // We already know about this group.  Pass the
             // corresponding native group pointer value to the native
             // create method.
-            groupMap.put(group,
-                         new Long(switchToGroup(groupPointer.longValue())));
+            switchToGroup(groupPointer.longValue());
           }
-        current_group = group;
       }
   }
 
   // Override the superclass postItemEvent so that the peer doesn't
   // need information that we have.
   // called back by native side: item_toggled_cb
-  synchronized public void postItemEvent(Object item, boolean state)
+  public synchronized void postItemEvent(Object item, boolean state)
   {
     // Only fire event is state actually changed.
     if (currentState != state)
@@ -222,8 +220,13 @@
 			    state ? ItemEvent.SELECTED : ItemEvent.DESELECTED);
       }
   }
+  
+  public synchronized void addToGroupMap(long groupPointer)
+  {
+    groupMap.put(current_group, new Long (groupPointer));
+  }
 
-  public void dispose ()
+  public synchronized void dispose ()
   {
     super.dispose ();
   }
Index: include/gnu_java_awt_peer_gtk_GtkCheckboxPeer.h
===================================================================
RCS file: /cvsroot/classpath/classpath/include/gnu_java_awt_peer_gtk_GtkCheckboxPeer.h,v
retrieving revision 1.11
diff -u -r1.11 gnu_java_awt_peer_gtk_GtkCheckboxPeer.h
--- include/gnu_java_awt_peer_gtk_GtkCheckboxPeer.h	30 Jun 2006 14:47:17 -0000	1.11
+++ include/gnu_java_awt_peer_gtk_GtkCheckboxPeer.h	30 Jun 2006 19:19:56 -0000
@@ -11,10 +11,10 @@
 #endif
 
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_createCheckButton (JNIEnv *env, jobject);
-JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_createRadioButton (JNIEnv *env, jobject, jlong);
-JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_addToGroup (JNIEnv *env, jobject, jlong);
-JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_removeFromGroup (JNIEnv *env, jobject);
-JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_switchToGroup (JNIEnv *env, jobject, jlong);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_createRadioButton (JNIEnv *env, jobject, jlong);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_addToGroup (JNIEnv *env, jobject, jlong);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_removeFromGroup (JNIEnv *env, jobject);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_switchToGroup (JNIEnv *env, jobject, jlong);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_connectSignals (JNIEnv *env, jobject);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_gtkWidgetModifyFont (JNIEnv *env, jobject, jstring, jint, jint);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_gtkButtonSetLabel (JNIEnv *env, jobject, jstring);
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkCheckboxPeer.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkCheckboxPeer.c,v
retrieving revision 1.26
diff -u -r1.26 gnu_java_awt_peer_gtk_GtkCheckboxPeer.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkCheckboxPeer.c	30 Jun 2006 14:47:17 -0000	1.26
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkCheckboxPeer.c	30 Jun 2006 19:19:57 -0000
@@ -43,6 +43,7 @@
 #include "jcl.h"
 
 static jmethodID postItemEventID;
+static jmethodID addToGroupMapID;
 static GtkWidget *checkbox_get_widget (GtkWidget *widget);
 static void item_toggled_cb (GtkToggleButton *item, jobject peer);
 
@@ -57,6 +58,10 @@
   postItemEventID = (*cp_gtk_gdk_env())->GetMethodID (cp_gtk_gdk_env(), gtkcheckboxpeer,
                                                "postItemEvent", 
                                                "(Ljava/lang/Object;Z)V");
+  
+  addToGroupMapID = (*cp_gtk_gdk_env())->GetMethodID (cp_gtk_gdk_env(), gtkcheckboxpeer,
+                                               "addToGroupMap", 
+                                               "(J)V");
 }
 
 JNIEXPORT void JNICALL
@@ -195,7 +200,7 @@
    This function is called when initially creating the
    button, so an eventbox is created.
  */
-JNIEXPORT jlong JNICALL 
+JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_createRadioButton 
   (JNIEnv *env, jobject obj, jlong groupPointer)
 {
@@ -211,8 +216,7 @@
   if (groupPointer != 0)
   {
     native_group = JLONG_TO_PTR (GSList, groupPointer);
-    if(! GTK_IS_RADIO_BUTTON (native_group->data))
-      native_group = NULL;
+    g_assert (GTK_IS_RADIO_BUTTON (native_group->data));
   }
   button = gtk_radio_button_new_with_label (native_group, "");
   
@@ -229,9 +233,11 @@
   
   NSA_SET_PTR (env, obj, eventbox);
   
-  gdk_threads_leave ();
+  (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), obj,
+                                addToGroupMapID,
+                                PTR_TO_JLONG (native_group));
   
-  return PTR_TO_JLONG (native_group);
+  gdk_threads_leave ();
 }
 
 /* Add the object to the group pointed to by groupPointer.
@@ -239,7 +245,7 @@
    a radio button. Otherwise, creating a radio button in an
    existing group.
  */
-JNIEXPORT jlong JNICALL 
+JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_addToGroup 
   (JNIEnv *env, jobject obj, jlong groupPointer)
 {
@@ -264,8 +270,7 @@
   if (groupPointer != 0)
     {
       native_group = JLONG_TO_PTR (GSList, groupPointer);
-      if(! GTK_IS_RADIO_BUTTON (native_group->data))
-        native_group = NULL;
+      g_assert (GTK_IS_RADIO_BUTTON (native_group->data));
     }
       
   radio_button = gtk_radio_button_new_with_label (native_group, label);
@@ -281,22 +286,20 @@
   }
              
   gtk_container_remove (GTK_CONTAINER (container), check_button);
-
-  NSA_DEL_PTR (env, obj);
-  NSA_SET_PTR (env, obj, container);
-  
   gtk_container_add (GTK_CONTAINER (container), radio_button);
   gtk_widget_show (radio_button);
   
-  gdk_threads_leave ();
+  (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), obj,
+                                addToGroupMapID,
+                                PTR_TO_JLONG (native_group));
   
-  return PTR_TO_JLONG (native_group);
+  gdk_threads_leave ();
 }
 
 /* Remove the object from the group pointed to by groupPointer.
    We are removing the radio button and creating a check button. 
  */
-JNIEXPORT jlong JNICALL 
+JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_removeFromGroup 
   (JNIEnv *env, jobject obj)
 {
@@ -324,25 +327,27 @@
              
   native_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_button));
   native_group = g_slist_remove (native_group, GTK_RADIO_BUTTON (radio_button));
-  GTK_RADIO_BUTTON(radio_button)->group = NULL;
   
-  gtk_container_remove (GTK_CONTAINER (container), radio_button);
-    
-  NSA_DEL_PTR (env, obj);
-  NSA_SET_PTR (env, obj, container);
+  if (native_group && ! GTK_IS_RADIO_BUTTON (native_group->data))
+    native_group = NULL;
   
+  GTK_RADIO_BUTTON(radio_button)->group = NULL;
+  
+  gtk_container_remove (GTK_CONTAINER (container), radio_button);  
   gtk_container_add (GTK_CONTAINER (container), check_button);
   gtk_widget_show (check_button);
   
-  gdk_threads_leave ();
+  (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), obj,
+                                addToGroupMapID,
+                                PTR_TO_JLONG (native_group));
   
-  return PTR_TO_JLONG (native_group);
+  gdk_threads_leave ();
 }
 
 /* Move the radio button to a new group. If groupPointer is
    0, create a new group.
  */
-JNIEXPORT jlong JNICALL 
+JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkCheckboxPeer_switchToGroup 
   (JNIEnv *env, jobject obj, jlong groupPointer)
 {
@@ -362,8 +367,7 @@
   if (groupPointer != 0)
   {
     native_group = JLONG_TO_PTR (GSList, groupPointer);
-    if(! GTK_IS_RADIO_BUTTON (native_group->data))
-      native_group = NULL;
+    g_assert (GTK_IS_RADIO_BUTTON (native_group->data));
   }
   gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), native_group);
   
@@ -374,9 +378,11 @@
     GTK_RADIO_BUTTON(radio_button)->group = native_group;
   }
    
-  gdk_threads_leave ();
+  (*cp_gtk_gdk_env())->CallVoidMethod (cp_gtk_gdk_env(), obj,
+                                addToGroupMapID,
+                                PTR_TO_JLONG (native_group));
   
-  return PTR_TO_JLONG (native_group);
+  gdk_threads_leave ();
 }
 
 static void

Reply via email to