6524352: support for high-resolution mouse wheel
Summary: added support for high-resolution mouse wheel
Reviewed-by: dav, son
--- a/jdk/src/share/classes/java/awt/Component.java Fri Mar 14 18:50:02 2008 +0300
+++ b/jdk/src/share/classes/java/awt/Component.java Fri Mar 14 20:40:09 2008 +0300
@@ -4604,7 +4604,8 @@
e.isPopupTrigger(),
e.getScrollType(),
e.getScrollAmount(),
- e.getWheelRotation());
+ e.getWheelRotation(),
+ e.getPreciseWheelRotation());
((AWTEvent)e).copyPrivateDataInto(newMWE);
// When dispatching a wheel event to
// ancestor, there is no need trying to find descendant
--- a/jdk/src/share/classes/java/awt/Container.java Fri Mar 14 18:50:02 2008 +0300
+++ b/jdk/src/share/classes/java/awt/Container.java Fri Mar 14 20:40:09 2008 +0300
@@ -4431,7 +4431,8 @@
e.isPopupTrigger(),
((MouseWheelEvent)e).getScrollType(),
((MouseWheelEvent)e).getScrollAmount(),
- ((MouseWheelEvent)e).getWheelRotation());
+ ((MouseWheelEvent)e).getWheelRotation(),
+ ((MouseWheelEvent)e).getPreciseWheelRotation());
}
else {
retargeted = new MouseEvent(target,
--- a/jdk/src/share/classes/java/awt/event/MouseWheelEvent.java Fri Mar 14 18:50:02 2008 +0300
+++ b/jdk/src/share/classes/java/awt/event/MouseWheelEvent.java Fri Mar 14 20:40:09 2008 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -74,6 +74,19 @@
* methods for conforming to the underlying platform settings. These
* platform settings can be changed at any time by the user. MouseWheelEvents
* reflect the most recent settings.
+ * <P>
+ * The <code>MouseWheelEvent</code> class includes methods for
+ * getting the number of "clicks" by which the mouse wheel is rotated.
+ * The {@link #getWheelRotation} method returns the integer number
+ * of "clicks" corresponding to the number of notches by which the wheel was
+ * rotated. In addition to this method, the <code>MouseWheelEvent</code>
+ * class provides the {@link #getPreciseWheelRotation} method which returns
+ * a double number of "clicks" in case a partial rotation occurred.
+ * The {@link #getPreciseWheelRotation} method is useful if a mouse supports
+ * a high-resolution wheel, such as a freely rotating wheel with no
+ * notches. Applications can benefit by using this method to process
+ * mouse wheel events more precisely, and thus, making visual perception
+ * smoother.
*
* @author Brent Christian
* @see MouseWheelListener
@@ -131,6 +144,13 @@
*/
int wheelRotation;
+ /**
+ * Indicates how far the mouse wheel was rotated.
+ *
+ * @see #getPreciseWheelRotation
+ */
+ double preciseWheelRotation;
+
/*
* serialVersionUID
*/
@@ -165,8 +185,8 @@
* <code>WHEEL_BLOCK_SCROLL</code>
* @param scrollAmount for scrollType <code>WHEEL_UNIT_SCROLL</code>,
* the number of units to be scrolled
- * @param wheelRotation the amount that the mouse wheel was rotated (the
- * number of "clicks")
+ * @param wheelRotation the integer number of "clicks" by which the mouse
+ * wheel was rotated
*
* @throws IllegalArgumentException if <code>source</code> is null
* @see MouseEvent#MouseEvent(java.awt.Component, int, long, int, int, int, int, boolean)
@@ -211,8 +231,8 @@
* <code>WHEEL_BLOCK_SCROLL</code>
* @param scrollAmount for scrollType <code>WHEEL_UNIT_SCROLL</code>,
* the number of units to be scrolled
- * @param wheelRotation the amount that the mouse wheel was rotated (the
- * number of "clicks")
+ * @param wheelRotation the integer number of "clicks" by which the mouse
+ * wheel was rotated
*
* @throws IllegalArgumentException if <code>source</code> is null
* @see MouseEvent#MouseEvent(java.awt.Component, int, long, int, int, int, int, boolean)
@@ -223,12 +243,68 @@
int x, int y, int xAbs, int yAbs, int clickCount, boolean popupTrigger,
int scrollType, int scrollAmount, int wheelRotation) {
+ this(source, id, when, modifiers, x, y, xAbs, yAbs, clickCount, popupTrigger,
+ scrollType, scrollAmount, wheelRotation, wheelRotation);
+
+ }
+
+
+ /**
+ * Constructs a <code>MouseWheelEvent</code> object with the specified
+ * source component, type, modifiers, coordinates, absolute coordinates,
+ * scroll type, scroll amount, and wheel rotation.
+ * <p>Note that passing in an invalid <code>id</code> parameter results
+ * in unspecified behavior. This method throws an
+ * <code>IllegalArgumentException</code> if <code>source</code> equals
+ * <code>null</code>.
+ * <p>Even if inconsistent values for relative and absolute coordinates
+ * are passed to the constructor, a <code>MouseWheelEvent</code> instance
+ * is still created and no exception is thrown.
+ *
+ * @param source the <code>Component</code> that originated the event
+ * @param id the integer value that identifies the event
+ * @param when a long value that gives the time when the event occurred
+ * @param modifiers the modifier keys down during event
+ * (shift, ctrl, alt, meta)
+ * @param x the horizontal <code>x</code> coordinate for the
+ * mouse location
+ * @param y the vertical <code>y</code> coordinate for the
+ * mouse location
+ * @param xAbs the absolute horizontal <code>x</code> coordinate for
+ * the mouse location
+ * @param yAbs the absolute vertical <code>y</code> coordinate for
+ * the mouse location
+ * @param clickCount the number of mouse clicks associated with the event
+ * @param popupTrigger a boolean value, <code>true</code> if this event is a trigger
+ * for a popup-menu
+ * @param scrollType the type of scrolling which should take place in
+ * response to this event; valid values are
+ * <code>WHEEL_UNIT_SCROLL</code> and
+ * <code>WHEEL_BLOCK_SCROLL</code>
+ * @param scrollAmount for scrollType <code>WHEEL_UNIT_SCROLL</code>,
+ * the number of units to be scrolled
+ * @param wheelRotation the integer number of "clicks" by which the mouse wheel
+ * was rotated
+ * @param preciseWheelRotation the double number of "clicks" by which the mouse wheel
+ * was rotated
+ *
+ * @throws IllegalArgumentException if <code>source</code> is null
+ * @see MouseEvent#MouseEvent(java.awt.Component, int, long, int, int, int, int, boolean)
+ * @see MouseEvent#MouseEvent(java.awt.Component, int, long, int, int, int, int, int, int, boolean, int)
+ * @since 1.7
+ */
+ public MouseWheelEvent (Component source, int id, long when, int modifiers,
+ int x, int y, int xAbs, int yAbs, int clickCount, boolean popupTrigger,
+ int scrollType, int scrollAmount, int wheelRotation, double preciseWheelRotation) {
+
super(source, id, when, modifiers, x, y, xAbs, yAbs, clickCount,
popupTrigger, MouseEvent.NOBUTTON);
this.scrollType = scrollType;
this.scrollAmount = scrollAmount;
this.wheelRotation = wheelRotation;
+ this.preciseWheelRotation = preciseWheelRotation;
+
}
/**
@@ -267,17 +343,35 @@
}
/**
- * Returns the number of "clicks" the mouse wheel was rotated.
+ * Returns the number of "clicks" the mouse wheel was rotated, as an integer.
+ * A partial rotation may occur if the mouse supports a high-resolution wheel.
+ * In this case, the method returns zero until a full "click" has been accumulated.
*
* @return negative values if the mouse wheel was rotated up/away from
* the user, and positive values if the mouse wheel was rotated down/
* towards the user
+ * @see #getPreciseWheelRotation
*/
public int getWheelRotation() {
return wheelRotation;
}
/**
+ * Returns the number of "clicks" the mouse wheel was rotated, as a double.
+ * A partial rotation may occur if the mouse supports a high-resolution wheel.
+ * In this case, the return value will include a fractional "click".
+ *
+ * @return negative values if the mouse wheel was rotated up or away from
+ * the user, and positive values if the mouse wheel was rotated down or
+ * towards the user
+ * @see #getWheelRotation
+ * @since 1.7
+ */
+ public double getPreciseWheelRotation() {
+ return preciseWheelRotation;
+ }
+
+ /**
* This is a convenience method to aid in the implementation of
* the common-case MouseWheelListener - to scroll a ScrollPane or
* JScrollPane by an amount which conforms to the platform settings.
@@ -348,6 +442,6 @@
}
return super.paramString()+",scrollType="+scrollTypeStr+
",scrollAmount="+getScrollAmount()+",wheelRotation="+
- getWheelRotation();
+ getWheelRotation()+",preciseWheelRotation="+getPreciseWheelRotation();
}
}
--- a/jdk/src/windows/native/sun/windows/awt_Component.cpp Fri Mar 14 18:50:02 2008 +0300
+++ b/jdk/src/windows/native/sun/windows/awt_Component.cpp Fri Mar 14 20:40:09 2008 +0300
@@ -226,6 +226,8 @@
CriticalSection windowMoveLock;
BOOL windowMoveLockHeld = FALSE;
+int AwtComponent::sm_wheelRotationAmount = 0;
+
/************************************************************************
* AwtComponent methods
*/
@@ -2074,6 +2076,8 @@
sm_realFocusOpposite = NULL;
}
+ sm_wheelRotationAmount = 0;
+
SendFocusEvent(java_awt_event_FocusEvent_FOCUS_GAINED, hWndLostFocus);
return mrDoDefault;
@@ -2105,6 +2109,7 @@
}
sm_focusOwner = NULL;
+ sm_wheelRotationAmount = 0;
SendFocusEvent(java_awt_event_FocusEvent_FOCUS_LOST, hWndGotFocus);
return mrDoDefault;
@@ -2622,9 +2627,13 @@
BOOL result;
UINT platformLines;
+ sm_wheelRotationAmount += wheelRotation;
+
// AWT interprets wheel rotation differently than win32, so we need to
// decode wheel amount.
- jint newWheelRotation = wheelRotation / (-1 * WHEEL_DELTA);
+ jint roundedWheelRotation = sm_wheelRotationAmount / (-1 * WHEEL_DELTA);
+ jdouble preciseWheelRotation = (jdouble) wheelRotation / (-1 * WHEEL_DELTA);
+
MSG msg;
if (IS_WIN95 && !IS_WIN98) {
@@ -2657,7 +2666,9 @@
SendMouseWheelEvent(java_awt_event_MouseEvent_MOUSE_WHEEL, TimeHelper::getMessageTimeUTC(),
eventPt.x, eventPt.y, GetJavaModifiers(), 0, 0, scrollType,
- scrollLines, newWheelRotation, &msg);
+ scrollLines, roundedWheelRotation, preciseWheelRotation, &msg);
+
+ sm_wheelRotationAmount %= WHEEL_DELTA;
return mrConsume;
}
@@ -4989,8 +5000,8 @@
AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y,
jint modifiers, jint clickCount,
jboolean popupTrigger, jint scrollType,
- jint scrollAmount, jint wheelRotation,
- MSG *pMsg)
+ jint scrollAmount, jint roundedWheelRotation,
+ jdouble preciseWheelRotation, MSG *pMsg)
{
/* Code based not so loosely on AwtComponent::SendMouseEvent */
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
@@ -5018,7 +5029,7 @@
if (mouseWheelEventConst == NULL) {
mouseWheelEventConst =
env->GetMethodID(mouseWheelEventCls, "<init>",
- "(Ljava/awt/Component;IJIIIIZIII)V");
+ "(Ljava/awt/Component;IJIIIIIIZIIID)V");
DASSERT(mouseWheelEventConst);
}
if (env->EnsureLocalCapacity(2) < 0) {
@@ -5026,14 +5037,16 @@
}
jobject target = GetTarget(env);
DTRACE_PRINTLN("creating MWE in JNI");
+
jobject mouseWheelEvent = env->NewObject(mouseWheelEventCls,
mouseWheelEventConst,
target,
id, when, modifiers,
x+insets.left, y+insets.top,
+ 0, 0,
clickCount, popupTrigger,
scrollType, scrollAmount,
- wheelRotation);
+ roundedWheelRotation, preciseWheelRotation);
if (safe_ExceptionOccurred(env)) {
env->ExceptionDescribe();
env->ExceptionClear();
--- a/jdk/src/windows/native/sun/windows/awt_Component.h Fri Mar 14 18:50:02 2008 +0300
+++ b/jdk/src/windows/native/sun/windows/awt_Component.h Fri Mar 14 20:40:09 2008 +0300
@@ -392,7 +392,7 @@
jint modifiers, jint clickCount,
jboolean popupTrigger, jint scrollType,
jint scrollAmount, jint wheelRotation,
- MSG *msg = NULL);
+ jdouble preciseWheelRotation, MSG *msg = NULL);
/*
* Allocate and initialize a new java.awt.event.FocusEvent, and
@@ -785,7 +785,9 @@
int windowMoveLockPosCX;
int windowMoveLockPosCY;
-private:
+ // 6524352: support finer-resolution
+ static int sm_wheelRotationAmount;
+
/*
* The association list of children's IDs and corresponding components.
* Some components like Choice or List are required their sizes while
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/event/MouseEvent/SmoothWheel/SmoothWheel.java Fri Mar 14 20:40:09 2008 +0300
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ @test %W% %E% %I%, %G%
+ @bug 6524352
+ @summary support for high-resolution mouse wheel
+ @author dmitry cherepanov: area=awt.event
+ @run main/manual SmoothWheel
+*/
+
+/**
+ * SmoothWheel.java
+ *
+ * summary:
+ */
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class SmoothWheel
+{
+
+ //*** test-writer defined static variables go here ***
+
+
+ private static void init()
+ {
+ String[] instructions =
+ {
+ "1. the test is for high-resolution mouse wheel only, ",
+ " refer to the cr# 6524352 for more info about such devices, ",
+ "2. you'll see a frame, the frame contains a checkbox, ",
+ "3. initially, the state of the checkbox is off, ",
+ " use mouse wheel over the frame, ",
+ " and the frame will change its size gradually, ",
+ "4. turn on the checkbox, ",
+ " use mouse wheel again over the frame, ",
+ " now the frame will change its size smoothly, ",
+ "5. if the frame has always the same size or",
+ " if the frame changes its size equally in 3,4 cases, ",
+ " then the test failed. Otherwise, it passed."
+ };
+
+ Sysout.createDialog( );
+ Sysout.printInstructions( instructions );
+
+ final Frame frame = new Frame();
+ final Checkbox checkbox = new Checkbox("smooth wheel?");
+ checkbox.setState(false);
+
+ frame.setLayout (new FlowLayout());
+ frame.add(checkbox);
+
+ frame.addMouseWheelListener(new MouseWheelListener() {
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ Sysout.println(e.toString());
+ double wheelRotation = 0;
+ if (checkbox.getState()) {
+ wheelRotation = e.getPreciseWheelRotation();
+ } else {
+ wheelRotation = e.getWheelRotation();
+ }
+ Dimension size = frame.getSize();
+ size.width += 10 * wheelRotation;
+ size.height += 10 * wheelRotation;
+ frame.setSize(size);
+ }
+ });
+
+ frame.setBounds(200, 200, 200, 200);
+ frame.setVisible(true);
+
+ }//End init()
+
+
+
+ /*****************************************************
+ * 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 ManualMainTest
+
+//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" )
+ {
+ SmoothWheel.pass();
+ }
+ else
+ {
+ SmoothWheel.fail();
+ }
+ }
+
+}// TestDialog class