6431076: Cursor gets reset to text cursor in xawt TextArea when autoscrolling on append
authordav
Tue, 25 Jan 2011 19:07:27 +0300
changeset 8133 c7e4a02e928e
parent 8132 8686fda1829a
child 8134 fb60ca70b79e
6431076: Cursor gets reset to text cursor in xawt TextArea when autoscrolling on append Reviewed-by: art, anthony
jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.java
jdk/src/solaris/classes/sun/awt/X11/XGlobalCursorManager.java
jdk/src/solaris/classes/sun/awt/X11/XTextAreaPeer.java
jdk/test/java/awt/TextArea/MouseOverScrollbarWhenTyping/Test.java
jdk/test/java/awt/TextArea/MouseOverScrollbarWhenTyping/Test1.java
--- a/jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.java	Tue Jan 25 15:33:14 2011 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.java	Tue Jan 25 19:07:27 2011 +0300
@@ -725,7 +725,21 @@
         XGlobalCursorManager.getCursorManager().updateCursorImmediately();
     }
 
-    public void pSetCursor(Cursor cursor) {
+    public final void pSetCursor(Cursor cursor) {
+        this.pSetCursor(cursor, true);
+    }
+
+    /*
+     * The method changes the cursor.
+     * @param cursor - a new cursor to change to.
+     * @param ignoreSubComponents - if {@code true} is passed then
+     *                              the new cursor will be installed on window.
+     *                              if {@code false} is passed then
+     *                              subsequent components will try to handle
+     *                              this request and install their cursor.
+     */
+    //ignoreSubComponents not used here
+    public void pSetCursor(Cursor cursor, boolean ignoreSubComponents) {
         XToolkit.awtLock();
         try {
             long xcursor = XGlobalCursorManager.getCursor(cursor);
--- a/jdk/src/solaris/classes/sun/awt/X11/XGlobalCursorManager.java	Tue Jan 25 15:33:14 2011 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XGlobalCursorManager.java	Tue Jan 25 19:07:27 2011 +0300
@@ -104,7 +104,9 @@
                     nativeContainer = new WeakReference<Component>(nc);
                 }
 
-                ((XComponentPeer)nc_peer).pSetCursor(cur);
+                //6431076. A subcomponents (a XTextArea in particular)
+                //may want to override the cursor over some of their parts.
+                ((XComponentPeer)nc_peer).pSetCursor(cur, false);
                 // in case of grab we do for Swing we need to update keep cursor updated
                 // (we don't need this in case of AWT menus).  Window Manager consider
                 // the grabber as a current window and use its cursor.  So we need to
--- a/jdk/src/solaris/classes/sun/awt/X11/XTextAreaPeer.java	Tue Jan 25 15:33:14 2011 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XTextAreaPeer.java	Tue Jan 25 19:07:27 2011 +0300
@@ -175,6 +175,34 @@
         super.dispose();
     }
 
+
+    /*
+     * The method overrides one from XComponentPeer
+     * If ignoreSubComponents=={@code true} it calls super.
+     * If ignoreSubComponents=={@code false} it uses the XTextArea machinery
+     * to change cursor appropriately. In particular it changes the cursor to
+     * default if over scrollbars.
+     */
+    @Override
+    public void pSetCursor(Cursor cursor, boolean ignoreSubComponents) {
+        Point onScreen = getLocationOnScreen();
+        if (ignoreSubComponents ||
+            javaMouseEventHandler == null ||
+            onScreen == null)
+        {
+            super.pSetCursor(cursor, true);
+            return;
+        }
+
+        Point cursorPos = new Point();
+        ((XGlobalCursorManager)XGlobalCursorManager.getCursorManager()).getCursorPos(cursorPos);
+
+        Point localPoint = new Point(cursorPos.x - onScreen.x, cursorPos.y - onScreen.y );
+
+        javaMouseEventHandler.setPointerToUnderPoint(localPoint);
+        javaMouseEventHandler.setCursor();
+    }
+
     void setScrollBarVisibility() {
         int visibility = ((TextArea)target).getScrollbarVisibility();
         jtext.setLineWrap(false);
@@ -1264,13 +1292,13 @@
         void handle( MouseEvent event ) {
             if ( ! grabbed ) {
                 // dispatch() needs up-to-date pointer in ungrabbed case.
-                setPointerToUnderEventPoint( event );
+                setPointerToUnderPoint( event.getPoint() );
             }
             dispatch( event );
             boolean wasGrabbed = grabbed;
             grabbed_update( event );
             if ( wasGrabbed && ! grabbed ) {
-                setPointerToUnderEventPoint( event );
+                setPointerToUnderPoint( event.getPoint() );
             }
             setCursor();
         }
@@ -1338,7 +1366,7 @@
                 // 'target.getCursor()' is also applied from elsewhere
                 // (at least now), but only when mouse "entered", and
                 // before 'XTextAreaPeer.handleJavaMouseEvent' is invoked.
-                outer.pSetCursor( outer.target.getCursor() );
+                outer.pSetCursor( outer.target.getCursor(), true );
             }
             else {
                 // We can write here a more intelligent cursor selection
@@ -1346,7 +1374,7 @@
                 // However, I see no point in doing so now. But if you feel
                 // like implementing it, you'll probably need to introduce
                 // 'Pointer.Type.PANEL'.
-                outer.pSetCursor( outer.textPane.getCursor() );
+                outer.pSetCursor( outer.textPane.getCursor(), true );
             }
         }
 
@@ -1391,8 +1419,7 @@
             return l;
         }
 
-        private void setPointerToUnderEventPoint( MouseEvent event ) {
-            Point point = event.getPoint();
+        private void setPointerToUnderPoint( Point point ) {
             if ( outer.textPane.getViewport().getBounds().contains( point ) ) {
                 current.setText();
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TextArea/MouseOverScrollbarWhenTyping/Test.java	Tue Jan 25 19:07:27 2011 +0300
@@ -0,0 +1,385 @@
+/*
+  @test
+  @bug 6431076
+  @summary Mouse cursor must remain DEFAULT over scrollbar when text is typed
+  @author Andrei Dmitriev: area=TextArea
+  @run main/manual Test
+*/
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class Test {
+    private static void init()  {
+        Frame f = new Frame("Test for cursor");
+        final int dim = 100;
+        String line = "";
+        for( int i=0; i<dim; ++i ) {
+            line += "a";
+        }
+        String text = "";
+        for( int i=0; i<dim; ++i ) {
+            text += line;
+            if( i < dim-1 ) {
+                text += "\n";
+            }
+        }
+
+        f.setLayout( new BorderLayout () );
+        f.add( new TextArea( text ) );
+        f.setSize(400, 300);
+
+        f.setVisible(true);
+
+        String[] instructions = {
+            "1. Place keyboard cursor inside TextArea.",
+            "2. Repeat steps 2.* for each of two TextArea's scrollbars.",
+            "2.1. Place mouse cursor over TextArea's scrollbar.",
+            "2.2. If mouse cursor is not DEFAULT_CURSOR (arrow), test failed.",
+            "2.3. Type any symbol into TextArea.",
+            "2.4. Type ENTER symbol into TextArea.",
+            "2.5. If mouse cursor changes to TEXT_CURSOR (beam), test failed",
+            "(if cursor disappears on Windows, it's OK).",
+            "3. Test passed.",
+        };
+
+        Sysout.createDialogWithInstructions( instructions );
+    }
+
+
+
+    /*****************************************************
+     * Standard Test Machinery Section
+     * DO NOT modify anything in this section -- it's a
+     * standard chunk of code which has all of the
+     * synchronisation necessary for the test harness.
+     * By keeping it the same in all tests, it is easier
+     * to read and understand someone else's test, as
+     * well as insuring that all tests behave correctly
+     * with the test harness.
+     * There is a section following this for test-defined
+     * classes
+     ******************************************************/
+    private static boolean theTestPassed = false;
+    private static boolean testGeneratedInterrupt = false;
+    private static String failureMessage = "";
+
+    private static Thread mainThread = null;
+
+    private static int sleepTime = 300000;
+
+    public static void main( String args[] ) throws InterruptedException
+    {
+        mainThread = Thread.currentThread();
+        try
+        {
+            init();
+        }
+        catch( TestPassedException e )
+        {
+            //The test passed, so just return from main and harness will
+            // interepret this return as a pass
+            return;
+        }
+        //At this point, neither test passed nor test failed has been
+        // called -- either would have thrown an exception and ended the
+        // test, so we know we have multiple threads.
+
+        //Test involves other threads, so sleep and wait for them to
+        // called pass() or fail()
+        try
+        {
+            Thread.sleep( sleepTime );
+            //Timed out, so fail the test
+            throw new RuntimeException( "Timed out after " + sleepTime/1000 + " seconds" );
+        }
+        catch (InterruptedException e)
+        {
+            if( ! testGeneratedInterrupt ) throw e;
+
+            //reset flag in case hit this code more than once for some reason (just safety)
+            testGeneratedInterrupt = false;
+            if ( theTestPassed == false )
+            {
+                throw new RuntimeException( failureMessage );
+            }
+        }
+
+    }//main
+
+    public static synchronized void setTimeoutTo( int seconds )
+    {
+        sleepTime = seconds * 1000;
+    }
+
+    public static synchronized void pass()
+    {
+        Sysout.println( "The test passed." );
+        Sysout.println( "The test is over, hit  Ctl-C to stop Java VM" );
+        //first check if this is executing in main thread
+        if ( mainThread == Thread.currentThread() )
+        {
+            //Still in the main thread, so set the flag just for kicks,
+            // and throw a test passed exception which will be caught
+            // and end the test.
+            theTestPassed = true;
+            throw new TestPassedException();
+        }
+        //pass was called from a different thread, so set the flag and interrupt
+        // the main thead.
+        theTestPassed = true;
+        testGeneratedInterrupt = true;
+        if (mainThread != null){
+            mainThread.interrupt();
+        }
+    }//pass()
+
+    public static synchronized void fail()
+    {
+        //test writer didn't specify why test failed, so give generic
+        fail( "it just plain failed! :-)" );
+    }
+
+    public static synchronized void fail( String whyFailed )
+    {
+        Sysout.println( "The test failed: " + whyFailed );
+        Sysout.println( "The test is over, hit  Ctl-C to stop Java VM" );
+        //check if this called from main thread
+        if ( mainThread == Thread.currentThread() )
+        {
+            //If main thread, fail now 'cause not sleeping
+            throw new RuntimeException( whyFailed );
+        }
+        theTestPassed = false;
+        testGeneratedInterrupt = true;
+        failureMessage = whyFailed;
+        mainThread.interrupt();
+    }//fail()
+
+}// class
+
+//This exception is used to exit from any level of call nesting
+// when it's determined that the test has passed, and immediately
+// end the test.
+class TestPassedException extends RuntimeException
+{
+}
+
+//*********** End Standard Test Machinery Section **********
+
+
+//************ Begin classes defined for the test ****************
+
+// make listeners in a class defined here, and instantiate them in init()
+
+/* Example of a class which may be written as part of a test
+class NewClass implements anInterface
+ {
+   static int newVar = 0;
+
+   public void eventDispatched(AWTEvent e)
+    {
+      //Counting events to see if we get enough
+      eventCount++;
+
+      if( eventCount == 20 )
+       {
+         //got enough events, so pass
+
+         ManualMainTest.pass();
+       }
+      else if( tries == 20 )
+       {
+         //tried too many times without getting enough events so fail
+
+         ManualMainTest.fail();
+       }
+
+    }// eventDispatched()
+
+ }// NewClass class
+
+*/
+
+
+//************** End classes defined for the test *******************
+
+
+
+
+/****************************************************
+ Standard Test Machinery
+ DO NOT modify anything below -- it's a standard
+  chunk of code whose purpose is to make user
+  interaction uniform, and thereby make it simpler
+  to read and understand someone else's test.
+ ****************************************************/
+
+/**
+ This is part of the standard test machinery.
+ It creates a dialog (with the instructions), and is the interface
+  for sending text messages to the user.
+ To print the instructions, send an array of strings to Sysout.createDialog
+  WithInstructions method.  Put one line of instructions per array entry.
+ To display a message for the tester to see, simply call Sysout.println
+  with the string to be displayed.
+ This mimics System.out.println but works within the test harness as well
+  as standalone.
+ */
+
+class Sysout
+{
+    private static TestDialog dialog;
+    private static boolean numbering = false;
+    private static int messageNumber = 0;
+
+    public static void createDialogWithInstructions( String[] instructions )
+    {
+        dialog = new TestDialog( new Frame(), "Instructions" );
+        dialog.printInstructions( instructions );
+        dialog.setVisible(true);
+        println( "Any messages for the tester will display here." );
+    }
+
+    public static void createDialog( )
+    {
+        dialog = new TestDialog( new Frame(), "Instructions" );
+        String[] defInstr = { "Instructions will appear here. ", "" } ;
+        dialog.printInstructions( defInstr );
+        dialog.setVisible(true);
+        println( "Any messages for the tester will display here." );
+    }
+
+
+    /* Enables message counting for the tester. */
+    public static void enableNumbering(boolean enable){
+        numbering = enable;
+    }
+
+    public static void printInstructions( String[] instructions )
+    {
+        dialog.printInstructions( instructions );
+    }
+
+
+    public static void println( String messageIn )
+    {
+        if (numbering) {
+            messageIn = "" + messageNumber + " " + messageIn;
+            messageNumber++;
+        }
+        dialog.displayMessage( messageIn );
+    }
+
+}// Sysout  class
+
+/**
+  This is part of the standard test machinery.  It provides a place for the
+   test instructions to be displayed, and a place for interactive messages
+   to the user to be displayed.
+  To have the test instructions displayed, see Sysout.
+  To have a message to the user be displayed, see Sysout.
+  Do not call anything in this dialog directly.
+  */
+class TestDialog extends Dialog implements ActionListener
+{
+
+    TextArea instructionsText;
+    TextArea messageText;
+    int maxStringLength = 80;
+    Panel  buttonP = new Panel();
+    Button passB = new Button( "pass" );
+    Button failB = new Button( "fail" );
+
+    //DO NOT call this directly, go through Sysout
+    public TestDialog( Frame frame, String name )
+    {
+        super( frame, name );
+        int scrollBoth = TextArea.SCROLLBARS_BOTH;
+        instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
+        add( "North", instructionsText );
+
+        messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
+        add("Center", messageText);
+
+        passB = new Button( "pass" );
+        passB.setActionCommand( "pass" );
+        passB.addActionListener( this );
+        buttonP.add( "East", passB );
+
+        failB = new Button( "fail" );
+        failB.setActionCommand( "fail" );
+        failB.addActionListener( this );
+        buttonP.add( "West", failB );
+
+        add( "South", buttonP );
+        pack();
+
+        setVisible(true);
+    }// TestDialog()
+
+    //DO NOT call this directly, go through Sysout
+    public void printInstructions( String[] instructions )
+    {
+        //Clear out any current instructions
+        instructionsText.setText( "" );
+
+        //Go down array of instruction strings
+
+        String printStr, remainingStr;
+        for( int i=0; i < instructions.length; i++ )
+        {
+            //chop up each into pieces maxSringLength long
+            remainingStr = instructions[ i ];
+            while( remainingStr.length() > 0 )
+            {
+                //if longer than max then chop off first max chars to print
+                if( remainingStr.length() >= maxStringLength )
+                {
+                    //Try to chop on a word boundary
+                    int posOfSpace = remainingStr.
+                        lastIndexOf( ' ', maxStringLength - 1 );
+
+                    if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
+
+                    printStr = remainingStr.substring( 0, posOfSpace + 1 );
+                    remainingStr = remainingStr.substring( posOfSpace + 1 );
+                }
+                //else just print
+                else
+                {
+                    printStr = remainingStr;
+                    remainingStr = "";
+                }
+
+                instructionsText.append( printStr + "\n" );
+
+            }// while
+
+        }// for
+
+    }//printInstructions()
+
+    //DO NOT call this directly, go through Sysout
+    public void displayMessage( String messageIn )
+    {
+        messageText.append( messageIn + "\n" );
+        System.out.println(messageIn);
+    }
+
+    //catch presses of the passed and failed buttons.
+    //simply call the standard pass() or fail() static methods of
+    //ManualMainTest
+    public void actionPerformed( ActionEvent e )
+    {
+        if( e.getActionCommand() == "pass" )
+        {
+            Test.pass();
+        }
+        else
+        {
+            Test.fail();
+        }
+    }
+
+}// TestDialog  class
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TextArea/MouseOverScrollbarWhenTyping/Test1.java	Tue Jan 25 19:07:27 2011 +0300
@@ -0,0 +1,386 @@
+/*
+  @test
+  @bug 6431076
+  @summary Mouse cursor must remain DEFAULT over scrollbar when text is typed
+  @author Andrei Dmitriev: area=TextArea
+  @run main/manual Test1
+*/
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class Test1 {
+    private static void init()  {
+        Frame f = new Frame("Test1 for cursor");
+        final int dim = 100;
+        String line = "";
+        for( int i=0; i<dim; ++i ) {
+            line += "a";
+        }
+        String text = "";
+        for( int i=0; i<dim; ++i ) {
+            text += line;
+            if( i < dim-1 ) {
+                text += "\n";
+            }
+        }
+
+        f.setLayout( new BorderLayout () );
+        f.add( new TextArea( text ) );
+        f.setSize(400, 300);
+        f.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+
+        f.setVisible(true);
+
+        String[] instructions = {
+            "1. Place keyboard cursor inside TextArea.",
+            "2. Repeat steps 2.* for each of two TextArea's scrollbars.",
+            "2.1. Place mouse cursor over TextArea's scrollbar.",
+            "2.2. If mouse cursor is not HAND_CURSOR, test failed.",
+            "2.3. Type any symbol into TextArea.",
+            "2.4. Type ENTER symbol into TextArea.",
+            "2.5. If mouse cursor changes, test failed",
+            "(if cursor disappears on Windows, it's OK).",
+            "3. Test passed.",
+        };
+
+        Sysout.createDialogWithInstructions( instructions );
+    }
+
+
+
+    /*****************************************************
+     * Standard Test Machinery Section
+     * DO NOT modify anything in this section -- it's a
+     * standard chunk of code which has all of the
+     * synchronisation necessary for the test harness.
+     * By keeping it the same in all tests, it is easier
+     * to read and understand someone else's test, as
+     * well as insuring that all tests behave correctly
+     * with the test harness.
+     * There is a section following this for test-defined
+     * classes
+     ******************************************************/
+    private static boolean theTestPassed = false;
+    private static boolean testGeneratedInterrupt = false;
+    private static String failureMessage = "";
+
+    private static Thread mainThread = null;
+
+    private static int sleepTime = 300000;
+
+    public static void main( String args[] ) throws InterruptedException
+    {
+        mainThread = Thread.currentThread();
+        try
+        {
+            init();
+        }
+        catch( TestPassedException e )
+        {
+            //The test passed, so just return from main and harness will
+            // interepret this return as a pass
+            return;
+        }
+        //At this point, neither test passed nor test failed has been
+        // called -- either would have thrown an exception and ended the
+        // test, so we know we have multiple threads.
+
+        //Test involves other threads, so sleep and wait for them to
+        // called pass() or fail()
+        try
+        {
+            Thread.sleep( sleepTime );
+            //Timed out, so fail the test
+            throw new RuntimeException( "Timed out after " + sleepTime/1000 + " seconds" );
+        }
+        catch (InterruptedException e)
+        {
+            if( ! testGeneratedInterrupt ) throw e;
+
+            //reset flag in case hit this code more than once for some reason (just safety)
+            testGeneratedInterrupt = false;
+            if ( theTestPassed == false )
+            {
+                throw new RuntimeException( failureMessage );
+            }
+        }
+
+    }//main
+
+    public static synchronized void setTimeoutTo( int seconds )
+    {
+        sleepTime = seconds * 1000;
+    }
+
+    public static synchronized void pass()
+    {
+        Sysout.println( "The test passed." );
+        Sysout.println( "The test is over, hit  Ctl-C to stop Java VM" );
+        //first check if this is executing in main thread
+        if ( mainThread == Thread.currentThread() )
+        {
+            //Still in the main thread, so set the flag just for kicks,
+            // and throw a test passed exception which will be caught
+            // and end the test.
+            theTestPassed = true;
+            throw new TestPassedException();
+        }
+        //pass was called from a different thread, so set the flag and interrupt
+        // the main thead.
+        theTestPassed = true;
+        testGeneratedInterrupt = true;
+        if (mainThread != null){
+            mainThread.interrupt();
+        }
+    }//pass()
+
+    public static synchronized void fail()
+    {
+        //test writer didn't specify why test failed, so give generic
+        fail( "it just plain failed! :-)" );
+    }
+
+    public static synchronized void fail( String whyFailed )
+    {
+        Sysout.println( "The test failed: " + whyFailed );
+        Sysout.println( "The test is over, hit  Ctl-C to stop Java VM" );
+        //check if this called from main thread
+        if ( mainThread == Thread.currentThread() )
+        {
+            //If main thread, fail now 'cause not sleeping
+            throw new RuntimeException( whyFailed );
+        }
+        theTestPassed = false;
+        testGeneratedInterrupt = true;
+        failureMessage = whyFailed;
+        mainThread.interrupt();
+    }//fail()
+
+}// class
+
+//This exception is used to exit from any level of call nesting
+// when it's determined that the test has passed, and immediately
+// end the test.
+class TestPassedException extends RuntimeException
+{
+}
+
+//*********** End Standard Test Machinery Section **********
+
+
+//************ Begin classes defined for the test ****************
+
+// make listeners in a class defined here, and instantiate them in init()
+
+/* Example of a class which may be written as part of a test
+class NewClass implements anInterface
+ {
+   static int newVar = 0;
+
+   public void eventDispatched(AWTEvent e)
+    {
+      //Counting events to see if we get enough
+      eventCount++;
+
+      if( eventCount == 20 )
+       {
+         //got enough events, so pass
+
+         ManualMainTest.pass();
+       }
+      else if( tries == 20 )
+       {
+         //tried too many times without getting enough events so fail
+
+         ManualMainTest.fail();
+       }
+
+    }// eventDispatched()
+
+ }// NewClass class
+
+*/
+
+
+//************** End classes defined for the test *******************
+
+
+
+
+/****************************************************
+ Standard Test Machinery
+ DO NOT modify anything below -- it's a standard
+  chunk of code whose purpose is to make user
+  interaction uniform, and thereby make it simpler
+  to read and understand someone else's test.
+ ****************************************************/
+
+/**
+ This is part of the standard test machinery.
+ It creates a dialog (with the instructions), and is the interface
+  for sending text messages to the user.
+ To print the instructions, send an array of strings to Sysout.createDialog
+  WithInstructions method.  Put one line of instructions per array entry.
+ To display a message for the tester to see, simply call Sysout.println
+  with the string to be displayed.
+ This mimics System.out.println but works within the test harness as well
+  as standalone.
+ */
+
+class Sysout
+{
+    private static TestDialog dialog;
+    private static boolean numbering = false;
+    private static int messageNumber = 0;
+
+    public static void createDialogWithInstructions( String[] instructions )
+    {
+        dialog = new TestDialog( new Frame(), "Instructions" );
+        dialog.printInstructions( instructions );
+        dialog.setVisible(true);
+        println( "Any messages for the tester will display here." );
+    }
+
+    public static void createDialog( )
+    {
+        dialog = new TestDialog( new Frame(), "Instructions" );
+        String[] defInstr = { "Instructions will appear here. ", "" } ;
+        dialog.printInstructions( defInstr );
+        dialog.setVisible(true);
+        println( "Any messages for the tester will display here." );
+    }
+
+
+    /* Enables message counting for the tester. */
+    public static void enableNumbering(boolean enable){
+        numbering = enable;
+    }
+
+    public static void printInstructions( String[] instructions )
+    {
+        dialog.printInstructions( instructions );
+    }
+
+
+    public static void println( String messageIn )
+    {
+        if (numbering) {
+            messageIn = "" + messageNumber + " " + messageIn;
+            messageNumber++;
+        }
+        dialog.displayMessage( messageIn );
+    }
+
+}// Sysout  class
+
+/**
+  This is part of the standard test machinery.  It provides a place for the
+   test instructions to be displayed, and a place for interactive messages
+   to the user to be displayed.
+  To have the test instructions displayed, see Sysout.
+  To have a message to the user be displayed, see Sysout.
+  Do not call anything in this dialog directly.
+  */
+class TestDialog extends Dialog implements ActionListener
+{
+
+    TextArea instructionsText;
+    TextArea messageText;
+    int maxStringLength = 80;
+    Panel  buttonP = new Panel();
+    Button passB = new Button( "pass" );
+    Button failB = new Button( "fail" );
+
+    //DO NOT call this directly, go through Sysout
+    public TestDialog( Frame frame, String name )
+    {
+        super( frame, name );
+        int scrollBoth = TextArea.SCROLLBARS_BOTH;
+        instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
+        add( "North", instructionsText );
+
+        messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
+        add("Center", messageText);
+
+        passB = new Button( "pass" );
+        passB.setActionCommand( "pass" );
+        passB.addActionListener( this );
+        buttonP.add( "East", passB );
+
+        failB = new Button( "fail" );
+        failB.setActionCommand( "fail" );
+        failB.addActionListener( this );
+        buttonP.add( "West", failB );
+
+        add( "South", buttonP );
+        pack();
+
+        setVisible(true);
+    }// TestDialog()
+
+    //DO NOT call this directly, go through Sysout
+    public void printInstructions( String[] instructions )
+    {
+        //Clear out any current instructions
+        instructionsText.setText( "" );
+
+        //Go down array of instruction strings
+
+        String printStr, remainingStr;
+        for( int i=0; i < instructions.length; i++ )
+        {
+            //chop up each into pieces maxSringLength long
+            remainingStr = instructions[ i ];
+            while( remainingStr.length() > 0 )
+            {
+                //if longer than max then chop off first max chars to print
+                if( remainingStr.length() >= maxStringLength )
+                {
+                    //Try to chop on a word boundary
+                    int posOfSpace = remainingStr.
+                        lastIndexOf( ' ', maxStringLength - 1 );
+
+                    if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
+
+                    printStr = remainingStr.substring( 0, posOfSpace + 1 );
+                    remainingStr = remainingStr.substring( posOfSpace + 1 );
+                }
+                //else just print
+                else
+                {
+                    printStr = remainingStr;
+                    remainingStr = "";
+                }
+
+                instructionsText.append( printStr + "\n" );
+
+            }// while
+
+        }// for
+
+    }//printInstructions()
+
+    //DO NOT call this directly, go through Sysout
+    public void displayMessage( String messageIn )
+    {
+        messageText.append( messageIn + "\n" );
+        System.out.println(messageIn);
+    }
+
+    //catch presses of the passed and failed buttons.
+    //simply call the standard pass() or fail() static methods of
+    //ManualMainTest
+    public void actionPerformed( ActionEvent e )
+    {
+        if( e.getActionCommand() == "pass" )
+        {
+            Test1.pass();
+        }
+        else
+        {
+            Test1.fail();
+        }
+    }
+
+}// TestDialog  class