From 20b2d7576f68893b7592e295742b49cd2ae61dfe Mon Sep 17 00:00:00 2001 From: dcherepanov Date: Fri, 14 Mar 2008 20:40:09 +0300 Subject: [PATCH] 6524352: support for high-resolution mouse wheel Summary: added support for high-resolution mouse wheel Reviewed-by: dav, son --- src/share/classes/java/awt/Component.java | 3 +- src/share/classes/java/awt/Container.java | 3 +- .../java/awt/event/MouseWheelEvent.java | 108 ++++- .../native/sun/windows/awt_Component.cpp | 25 +- .../native/sun/windows/awt_Component.h | 6 +- .../MouseEvent/SmoothWheel/SmoothWheel.java | 432 ++++++++++++++++++ 6 files changed, 560 insertions(+), 17 deletions(-) create mode 100644 test/java/awt/event/MouseEvent/SmoothWheel/SmoothWheel.java diff --git a/src/share/classes/java/awt/Component.java b/src/share/classes/java/awt/Component.java index 5435d9b25..718cafe06 100644 --- a/src/share/classes/java/awt/Component.java +++ b/src/share/classes/java/awt/Component.java @@ -4604,7 +4604,8 @@ public abstract class Component implements ImageObserver, MenuContainer, 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 diff --git a/src/share/classes/java/awt/Container.java b/src/share/classes/java/awt/Container.java index 46164c410..4a347f3d5 100644 --- a/src/share/classes/java/awt/Container.java +++ b/src/share/classes/java/awt/Container.java @@ -4431,7 +4431,8 @@ class LightweightDispatcher implements java.io.Serializable, AWTEventListener { e.isPopupTrigger(), ((MouseWheelEvent)e).getScrollType(), ((MouseWheelEvent)e).getScrollAmount(), - ((MouseWheelEvent)e).getWheelRotation()); + ((MouseWheelEvent)e).getWheelRotation(), + ((MouseWheelEvent)e).getPreciseWheelRotation()); } else { retargeted = new MouseEvent(target, diff --git a/src/share/classes/java/awt/event/MouseWheelEvent.java b/src/share/classes/java/awt/event/MouseWheelEvent.java index 40af56c44..a1c5b2ebb 100644 --- a/src/share/classes/java/awt/event/MouseWheelEvent.java +++ b/src/share/classes/java/awt/event/MouseWheelEvent.java @@ -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 @@ import java.awt.Component; * 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. + *

+ * The MouseWheelEvent 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 MouseWheelEvent + * 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 @@ public class MouseWheelEvent extends MouseEvent { */ int wheelRotation; + /** + * Indicates how far the mouse wheel was rotated. + * + * @see #getPreciseWheelRotation + */ + double preciseWheelRotation; + /* * serialVersionUID */ @@ -165,8 +185,8 @@ public class MouseWheelEvent extends MouseEvent { * WHEEL_BLOCK_SCROLL * @param scrollAmount for scrollType WHEEL_UNIT_SCROLL, * 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 source is null * @see MouseEvent#MouseEvent(java.awt.Component, int, long, int, int, int, int, boolean) @@ -211,8 +231,8 @@ public class MouseWheelEvent extends MouseEvent { * WHEEL_BLOCK_SCROLL * @param scrollAmount for scrollType WHEEL_UNIT_SCROLL, * 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 source is null * @see MouseEvent#MouseEvent(java.awt.Component, int, long, int, int, int, int, boolean) @@ -223,12 +243,68 @@ public class MouseWheelEvent extends MouseEvent { 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 MouseWheelEvent object with the specified + * source component, type, modifiers, coordinates, absolute coordinates, + * scroll type, scroll amount, and wheel rotation. + *

Note that passing in an invalid id parameter results + * in unspecified behavior. This method throws an + * IllegalArgumentException if source equals + * null. + *

Even if inconsistent values for relative and absolute coordinates + * are passed to the constructor, a MouseWheelEvent instance + * is still created and no exception is thrown. + * + * @param source the Component 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 x coordinate for the + * mouse location + * @param y the vertical y coordinate for the + * mouse location + * @param xAbs the absolute horizontal x coordinate for + * the mouse location + * @param yAbs the absolute vertical y coordinate for + * the mouse location + * @param clickCount the number of mouse clicks associated with the event + * @param popupTrigger a boolean value, true 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 + * WHEEL_UNIT_SCROLL and + * WHEEL_BLOCK_SCROLL + * @param scrollAmount for scrollType WHEEL_UNIT_SCROLL, + * 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 source 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,16 +343,34 @@ public class MouseWheelEvent extends MouseEvent { } /** - * 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 @@ -348,6 +442,6 @@ public class MouseWheelEvent extends MouseEvent { } return super.paramString()+",scrollType="+scrollTypeStr+ ",scrollAmount="+getScrollAmount()+",wheelRotation="+ - getWheelRotation(); + getWheelRotation()+",preciseWheelRotation="+getPreciseWheelRotation(); } } diff --git a/src/windows/native/sun/windows/awt_Component.cpp b/src/windows/native/sun/windows/awt_Component.cpp index e1b3a7d1d..9a9082b50 100644 --- a/src/windows/native/sun/windows/awt_Component.cpp +++ b/src/windows/native/sun/windows/awt_Component.cpp @@ -226,6 +226,8 @@ BOOL AwtComponent::m_QueryNewPaletteCalled = FALSE; CriticalSection windowMoveLock; BOOL windowMoveLockHeld = FALSE; +int AwtComponent::sm_wheelRotationAmount = 0; + /************************************************************************ * AwtComponent methods */ @@ -2074,6 +2076,8 @@ MsgRouting AwtComponent::WmSetFocus(HWND hWndLostFocus) sm_realFocusOpposite = NULL; } + sm_wheelRotationAmount = 0; + SendFocusEvent(java_awt_event_FocusEvent_FOCUS_GAINED, hWndLostFocus); return mrDoDefault; @@ -2105,6 +2109,7 @@ MsgRouting AwtComponent::WmKillFocus(HWND hWndGotFocus) } sm_focusOwner = NULL; + sm_wheelRotationAmount = 0; SendFocusEvent(java_awt_event_FocusEvent_FOCUS_LOST, hWndGotFocus); return mrDoDefault; @@ -2622,9 +2627,13 @@ MsgRouting AwtComponent::WmMouseWheel(UINT flags, int x, int y, 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 @@ MsgRouting AwtComponent::WmMouseWheel(UINT flags, int x, int y, 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 @@ void 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 @@ AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y, if (mouseWheelEventConst == NULL) { mouseWheelEventConst = env->GetMethodID(mouseWheelEventCls, "", - "(Ljava/awt/Component;IJIIIIZIII)V"); + "(Ljava/awt/Component;IJIIIIIIZIIID)V"); DASSERT(mouseWheelEventConst); } if (env->EnsureLocalCapacity(2) < 0) { @@ -5026,14 +5037,16 @@ AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y, } 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(); diff --git a/src/windows/native/sun/windows/awt_Component.h b/src/windows/native/sun/windows/awt_Component.h index cecab3983..ecc7ef5ea 100644 --- a/src/windows/native/sun/windows/awt_Component.h +++ b/src/windows/native/sun/windows/awt_Component.h @@ -392,7 +392,7 @@ public: 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 @@ private: 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 diff --git a/test/java/awt/event/MouseEvent/SmoothWheel/SmoothWheel.java b/test/java/awt/event/MouseEvent/SmoothWheel/SmoothWheel.java new file mode 100644 index 000000000..00e07c84d --- /dev/null +++ b/test/java/awt/event/MouseEvent/SmoothWheel/SmoothWheel.java @@ -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 -- GitLab