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