This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Modal FileDialog implementation.


This patch implements the AWT FileDialog with modality. It also paves the way to modality for general Dialogs as well (the part where show() blocks).

The initial size of the FileDialog window still has to be fixed. It is independent of the issues I was addressing and other people have been working on these aspects.

May thanks to Tom Fitzsimmons for all his help.


Note: I did not add more MethodIDs to the GtkMainThread. I found out that we are violating a JNI spec that says the MethodID _must_ be obtained with the same Class from where it will be used (if you think of it, this is necessary because of overriding methods). And indeed I found a case where our JNI implementation did not call an overriding method because the MethodID was obtained with a different class in gtkInit. Instead of creating many IDs for a method in GtkMainThread, one for each class from where it can be called from, I am proposing that we do create it from the class where it will be called. It will be done once when a widget is created, but compared with the cost of the creation of the widget itself it should be negligible. If you prefer to have these in GtkMainThread let me know and I will change the patch.



2004-01-07 Fernando Nasser <fnasser@redhat.com>


        * gnu/java/awt/peer/gtk/GtkFileDialogPeer.java (nativeSetFile):
        New name for the former setFile native method.
        (setFile): New method.
        (setDirectory): Implemented.
        (connectSignals): New native method.
        (setFilenameFilter): Improve comment.
        (getGraphics): Comment.
        (gtkHideFileDialog): New method.
        (gtkDisposeFileDialog): New method.
        (gtkSetFilename): New method.
        * java/awt/Dialog.java (show): Block on modal dialogs, but only
        for FileDialog for now.
        (hide): New method.
        (dispose): New method.
        * jni/gtk-peer/gnu_java_awt_peer_gtk_GtkFileDialogPeer.c
        (Java_gnu_java_awt_peer_gtk_GtkFileDialog_create): Replace
        deprecated creation functions.  Make dialog modal.  Add it to the
        window group.
        (Java_gnu_java_awt_peer_gtk_GtkFileDialog_connectSignals): New
        function.

(Java_gnu_java_awt_peer_gtk_GtkFileDialogPeer_gtkFileSelectionSetFilename):
        Rename to...
        (Java_gnu_java_awt_peer_gtk_GtkFileDialogPeer_nativeSetFile): New
        name.
        (window_closed): New function.
        (ok_clicked): New function.
        (cancel_clicked): New function.


-- Fernando Nasser Red Hat Canada Ltd. E-Mail: fnasser@redhat.com 2323 Yonge Street, Suite #300 Toronto, Ontario M4P 2C9
Index: gnu/java/awt/peer/gtk/GtkFileDialogPeer.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java,v
retrieving revision 1.3
diff -c -p -r1.3 GtkFileDialogPeer.java
*** gnu/java/awt/peer/gtk/GtkFileDialogPeer.java	11 Dec 2003 13:50:50 -0000	1.3
--- gnu/java/awt/peer/gtk/GtkFileDialogPeer.java	8 Jan 2004 03:13:01 -0000
*************** exception statement from your version. *
*** 38,50 ****
--- 38,58 ----
  
  package gnu.java.awt.peer.gtk;
  
+ import java.awt.AWTEvent;
+ import java.awt.Dialog;
  import java.awt.FileDialog;
  import java.awt.Graphics;
+ import java.awt.event.WindowEvent;
  import java.awt.peer.FileDialogPeer;
  import java.io.FilenameFilter;
  
  public class GtkFileDialogPeer extends GtkDialogPeer implements FileDialogPeer
  {
+   static final String FS = System.getProperty("file.separator");
+   
+   private String currentFile = null;
+   private String currentDirectory = null;
+ 
    native void create ();
  
    public GtkFileDialogPeer (FileDialog fd)
*************** public class GtkFileDialogPeer extends G
*** 52,72 ****
      super (fd);
    }
  
!   public void setDirectory (String directory)
    {
!     setFile (directory);
    }
  
!   public native void setFile (String file);
!   public native void connectJObject ();
  
    public void setFilenameFilter (FilenameFilter filter)
    {
!     /* GTK has no filters. */
    }
  
    public Graphics getGraphics ()
    {
      return null;
    }
  }
--- 60,194 ----
      super (fd);
    }
  
!   native void connectJObject ();
!   native void connectSignals ();
!   native void nativeSetFile (String file);
! 
!   public void setFile (String fileName)
    {
!     /* If nothing changed do nothing.  This usually happens because
!        the only way we have to set the file name in FileDialog is by
!        calling its SetFile which will call us back. */
!     if (((fileName == null) && (currentFile == null)) ||
!         ((fileName != null) && fileName.equals (currentFile)))
!       return;
! 
!     if ((fileName == null) || fileName.equals (""))
!       {
!         currentFile = "";
!         nativeSetFile ("");
!         return;
!       }
! 
!     // Remove any directory path from the filename
!     int sepIndex = fileName.lastIndexOf (FS);
!     if (sepIndex < 0)
!       {
!         currentFile = fileName;
!         nativeSetFile (fileName);
!       }
!     else
!       {
!         if (fileName.length() > (sepIndex + 1))
! 	  {
! 	    String fn = fileName.substring (sepIndex + 1);
!             currentFile = fn;
!             nativeSetFile (fn);
! 	  }
! 	else
! 	  {
!             currentFile = "";
!             nativeSetFile ("");
! 	  }
!       }
    }
  
!   public void setDirectory (String directory)
!   {
!     /* If nothing changed so nothing.  This usually happens because
!        the only way we have to set the directory in FileDialog is by
!        calling its setDirectory which will call us back. */
!     if (((directory == null) && (currentDirectory == null)) ||
!         ((directory != null) && directory.equals (currentDirectory)))
!       return;
! 
!     if ((directory == null) || directory.equals (""))
!       {
!         currentDirectory = FS;
!         nativeSetFile (FS);
! 	return;
!       }
!       
!     currentDirectory = directory;
! 
!     // Gtk expects the directory to end with a file separator
!     if (directory.substring (directory.length () - 1).equals (FS))
!       nativeSetFile (directory);
!     else
!       nativeSetFile (directory + FS);
!   }
  
    public void setFilenameFilter (FilenameFilter filter)
    {
!     /* GTK has no filter callbacks yet.  It works by setting a pattern
!      * (see gtk_file_selection_complete), which we can't convert
!      * to the callback paradigm. With GTK-2.4 there will be a
!      * gtk_file_filter_add_custom function that we can use. */
    }
  
    public Graphics getGraphics ()
    {
+     // GtkFileDialog will repaint by itself
      return null;
+   }
+   
+   void gtkHideFileDialog () 
+   {
+     ((Dialog) awtComponent).hide();
+   }
+   
+   void gtkDisposeFileDialog () 
+   {
+     ((Dialog) awtComponent).dispose();
+   }
+ 
+   /* Callback to set the file and directory values when the user is finished
+    * with the dialog.
+    */
+   void gtkSetFilename (String fileName)
+   {
+     FileDialog fd = (FileDialog) awtWidget;
+     if (fileName == null)
+       {
+         currentFile = null;
+         fd.setFile(null);
+         return;
+       }
+ 
+     int sepIndex = fileName.lastIndexOf (FS);
+     if (sepIndex < 0)
+       {
+         /* This should never happen on Unix (all paths start with '/') */
+ 	currentFile = fileName;
+         fd.setFile(fileName);
+       }
+     else
+       {
+         if (fileName.length() > (sepIndex + 1))
+ 	  {
+ 	    String fn = fileName.substring (sepIndex + 1);
+ 	    currentFile = fn;
+             fd.setFile(fn);
+ 	  }
+ 	else
+ 	  {
+             currentFile = null;
+             fd.setFile(null);
+ 	  }
+ 
+         String dn = fileName.substring (0, sepIndex + 1);
+         currentDirectory = dn;
+         fd.setDirectory(dn);
+       }
    }
  }
Index: java/awt/Dialog.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/awt/Dialog.java,v
retrieving revision 1.11
diff -c -p -r1.11 Dialog.java
*** java/awt/Dialog.java	5 Jan 2004 21:23:12 -0000	1.11
--- java/awt/Dialog.java	8 Jan 2004 03:13:01 -0000
*************** private boolean resizable = true;
*** 78,87 ****
    */
  private String title;
  
!   /**
!    * This field indicates whether the dialog is undecorated or not.
!    */
!   private boolean undecorated = false;
  
  /*************************************************************************/
  
--- 78,92 ----
    */
  private String title;
  
! /**
!   * This field indicates whether the dialog is undecorated or not.
!   */
! private boolean undecorated = false;
! 
! /**
!   * Indicates that we are blocked for modality in show
!   */
! private boolean blocked = false;
  
  /*************************************************************************/
  
*************** addNotify()
*** 380,390 ****
  
  /**
    * Makes this dialog visible and brings it to the front.
    */
! public void
  show()
  {
    super.show();
  }
  
  /*************************************************************************/
--- 385,462 ----
  
  /**
    * Makes this dialog visible and brings it to the front.
+   * If the dialog is modal and is not already visible, this call will not
+   *  return until the dialog is hidden by someone calling hide or dispose.
+   * If this is the event dispatching thread we must ensure that another event
+   *  thread runs while the one which invoked this method is blocked. 
    */
! public synchronized void
  show()
  {
    super.show();
+   if (isModal())
+     {
+       // If already shown (and blocked) just return
+       if (blocked)
+ 	return;
+ 
+       /* FIXME: Currently this thread may block forever if it called from
+          the event dispatch thread, so we only do this for FileDialog which
+          only depends on a signal which is delivered in the Gtk thread.
+          Remove this test when we add code to start another event
+          dispatch thread. */
+       if ((Thread.currentThread () instanceof EventDispatchThread) &&
+           !(this instanceof FileDialog))
+         return;
+       
+       try 
+         {
+ 	  blocked = true;
+ 	  wait ();
+ 	  blocked = false;
+         } 
+       catch (InterruptedException e)
+         {
+ 	  blocked = false;
+ 	  return;
+         }
+     }  
+ }
+ 
+ /*************************************************************************/
+ 
+ /**
+   * Hides the Dialog and then
+   * causes show() to return if it is currently blocked.
+   */
+ 
+ public synchronized void 
+ hide ()
+ {
+   if (blocked)
+     {
+       notifyAll ();
+     }
+ 
+   super.hide();
+ }
+ 
+ /*************************************************************************/
+ 
+ /**
+   * Disposes the Dialog and then causes show() to return
+   * if it is currently blocked.
+   */
+ 
+ public synchronized void 
+ dispose ()
+ {
+   if (blocked)
+     {
+       notifyAll ();
+     }
+ 
+   super.dispose();
  }
  
  /*************************************************************************/
Index: jni/gtk-peer/gnu_java_awt_peer_gtk_GtkFileDialogPeer.c
===================================================================
RCS file: /cvs/gcc/gcc/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkFileDialogPeer.c,v
retrieving revision 1.3
diff -c -p -r1.3 gnu_java_awt_peer_gtk_GtkFileDialogPeer.c
*** jni/gtk-peer/gnu_java_awt_peer_gtk_GtkFileDialogPeer.c	13 Dec 2003 01:15:47 -0000	1.3
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkFileDialogPeer.c	8 Jan 2004 03:13:01 -0000
*************** exception statement from your version. *
*** 37,44 ****
--- 37,53 ----
  
  
  #include "gtkpeer.h"
+ #include "gnu_java_awt_peer_gtk_GtkComponentPeer.h"
  #include "gnu_java_awt_peer_gtk_GtkFileDialogPeer.h"
  
+ static void window_closed (GtkDialog *dialog __attribute__((unused)),
+                            gint responseId,
+                            jobject peer_obj);
+ static void ok_clicked (GtkButton *button __attribute__((unused)),
+ 	                jobject peer_obj);
+ static void cancel_clicked (GtkButton *button __attribute__((unused)),
+ 	                    jobject peer_obj);
+ 
  /*
   * Make a new file selection dialog
   */
*************** Java_gnu_java_awt_peer_gtk_GtkFileDialog
*** 54,60 ****
  
    gdk_threads_enter ();
    
!   widget = gtk_type_new (gtk_file_selection_get_type ());
  
    gdk_threads_leave ();
  
--- 63,75 ----
  
    gdk_threads_enter ();
    
!   widget = gtk_file_selection_new ("");
!   /* GtkFileSelect is not modal by default */
!   gtk_window_set_modal (GTK_WINDOW (widget), TRUE);
! 
!   /* We must add this window to the group so input in the others are
!      disable while it is being shown */
!   gtk_window_group_add_window (global_gtk_window_group, GTK_WINDOW (widget));
  
    gdk_threads_leave ();
  
*************** Java_gnu_java_awt_peer_gtk_GtkFileDialog
*** 76,87 ****
    gdk_threads_leave ();
  }
  
  /*
   * Set the filename in the file selection dialog.
   */
  
  JNIEXPORT void JNICALL 
! Java_gnu_java_awt_peer_gtk_GtkFileDialogPeer_gtkFileSelectionSetFilename 
      (JNIEnv *env, jobject obj, jstring filename)
  {
    void *ptr;
--- 91,134 ----
    gdk_threads_leave ();
  }
  
+ JNIEXPORT void JNICALL 
+ Java_gnu_java_awt_peer_gtk_GtkFileDialogPeer_connectSignals
+   (JNIEnv *env, jobject obj)
+ {
+   void *ptr = NSA_GET_PTR (env, obj);
+   jobject *gref = NSA_GET_GLOBAL_REF (env, obj);
+   g_assert (gref);
+ 
+   gdk_threads_enter ();
+ 
+   gtk_widget_realize (GTK_WIDGET (ptr));
+ 
+   /* connect buttons to handlers */
+ 
+   g_signal_connect (G_OBJECT (GTK_DIALOG (ptr)),
+                     "response", 
+ 		    GTK_SIGNAL_FUNC (window_closed), *gref);
+ 
+   g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (ptr)->ok_button),
+                     "clicked", 
+ 		    GTK_SIGNAL_FUNC (ok_clicked), *gref);
+ 
+   g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (ptr)->cancel_button),
+                     "clicked", 
+ 		    GTK_SIGNAL_FUNC (cancel_clicked), *gref);
+ 		    
+   gdk_threads_leave ();
+ 
+   /* Connect the superclass signals.  */
+   Java_gnu_java_awt_peer_gtk_GtkComponentPeer_connectSignals (env, obj);
+ }
+ 
  /*
   * Set the filename in the file selection dialog.
   */
  
  JNIEXPORT void JNICALL 
! Java_gnu_java_awt_peer_gtk_GtkFileDialogPeer_nativeSetFile 
      (JNIEnv *env, jobject obj, jstring filename)
  {
    void *ptr;
*************** Java_gnu_java_awt_peer_gtk_GtkFileDialog
*** 89,97 ****
  
    ptr = NSA_GET_PTR (env, obj);
      
!   str = (*env)->GetStringUTFChars (env, filename, 0);      
    gdk_threads_enter ();
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (ptr), str);
    gdk_threads_leave ();
    (*env)->ReleaseStringUTFChars (env, filename, str);
  }
--- 136,247 ----
  
    ptr = NSA_GET_PTR (env, obj);
      
!   str = (*env)->GetStringUTFChars (env, filename, 0);
!      
    gdk_threads_enter ();
+ 
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (ptr), str);
+ 
    gdk_threads_leave ();
+ 
    (*env)->ReleaseStringUTFChars (env, filename, str);
  }
+ 
+ static void
+ window_closed (GtkDialog *dialog __attribute__((unused)),
+                gint responseId,
+                jobject peer_obj)
+ {
+   static int isIDSet = 0;
+   static jmethodID disposeID;
+   void *ptr;
+ 
+   // We only need this for the case when the user closed the window
+   if (responseId != GTK_RESPONSE_DELETE_EVENT)
+     return;
+ 
+   ptr = NSA_GET_PTR (gdk_env, peer_obj);
+   
+   if (!isIDSet)
+     {
+       jclass cx = (*gdk_env)->GetObjectClass (gdk_env, peer_obj);
+       disposeID = (*gdk_env)->GetMethodID (gdk_env, cx, "gtkDisposeFileDialog", "()V");
+       isIDSet = 1;
+     }
+     
+   gdk_threads_leave ();
+ 
+   /* We can dispose of the dialog now (and unblock show) */
+   (*gdk_env)->CallVoidMethod (gdk_env, peer_obj, disposeID);
+ 
+   gdk_threads_enter ();
+ }
+ 
+ static void
+ ok_clicked (GtkButton *button __attribute__((unused)),
+ 	                jobject peer_obj)
+ {
+   static int isIDSet = 0;
+   static jmethodID gtkSetFilenameID;
+   static jmethodID hideID;
+   void *ptr;
+   G_CONST_RETURN gchar *fileName;
+ 
+   ptr = NSA_GET_PTR (gdk_env, peer_obj);
+   
+   fileName = gtk_file_selection_get_filename (
+                GTK_FILE_SELECTION (GTK_WIDGET (ptr)));
+   
+   if (!isIDSet)
+     {
+       jclass cx = (*gdk_env)->GetObjectClass (gdk_env, peer_obj);
+       hideID = (*gdk_env)->GetMethodID (gdk_env, cx, "gtkHideFileDialog", "()V");
+       gtkSetFilenameID = (*gdk_env)->GetMethodID (gdk_env, cx,
+                                    "gtkSetFilename", "(Ljava.lang.String;)V");
+       isIDSet = 1;
+     }
+     
+   gdk_threads_leave ();
+   
+   /* Set the Java object field 'file' with this value. */
+   jstring str_fileName = (*gdk_env)->NewStringUTF (gdk_env, fileName);
+   (*gdk_env)->CallVoidMethod (gdk_env, peer_obj, gtkSetFilenameID, str_fileName);
+ 
+   /* We can hide the dialog now (and unblock show) */
+   (*gdk_env)->CallVoidMethod (gdk_env, peer_obj, hideID);
+ 
+   gdk_threads_enter ();
+ }
+ 
+ static void
+ cancel_clicked (GtkButton *button __attribute__((unused)),
+ 	                    jobject peer_obj)
+ {
+   static int isIDSet = 0;
+   static jmethodID gtkSetFilenameID;
+   static jmethodID hideID;
+   void *ptr;
+ 
+   ptr = NSA_GET_PTR (gdk_env, peer_obj);
+   
+   if (!isIDSet)
+     {
+       jclass cx = (*gdk_env)->GetObjectClass (gdk_env, peer_obj);
+       hideID = (*gdk_env)->GetMethodID (gdk_env, cx, "gtkHideFileDialog", "()V");
+       gtkSetFilenameID = (*gdk_env)->GetMethodID (gdk_env, cx,
+                                    "gtkSetFilename", "(Ljava.lang.String;)V");
+       isIDSet = 1;
+     }
+     
+   gdk_threads_leave ();
+ 
+   /* Set the Java object field 'file' with the null value. */
+   (*gdk_env)->CallVoidMethod (gdk_env, peer_obj, gtkSetFilenameID, NULL);
+ 
+   /* We can hide the dialog now (and unblock show) */
+   (*gdk_env)->CallVoidMethod (gdk_env, peer_obj, hideID);
+ 
+   gdk_threads_enter ();
+ }
+ 
+ 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]